/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2014 by Chiwen Chang * * 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 "config.h" #include "fixedpoint.h" #include "fracmul.h" #include "settings.h" #include "dsp_proc_entry.h" #include "dsp_misc.h" #include "dsp_filter.h" #include "core_alloc.h" /* Perceptual bass enhancement */ #define B3_DLY 72 /* ~800 uS */ #define B2_DLY 180 /* ~2000 uS*/ #define B0_DLY 276 /* ~3050 uS */ #define B3_SIZE (B3_DLY+1) #define B2_SIZE (B2_DLY+1) #define B0_SIZE (B0_DLY+1) static int pbe_strength = 100; static int pbe_precut = 0; static int32_t tcoef1, tcoef2, tcoef3; static int32_t *b0[2], *b2[2], *b3[2]; static int b0_r[2],b2_r[2],b3_r[2],b0_w[2],b2_w[2],b3_w[2]; int32_t temp_buffer; static struct dsp_filter pbe_filter[5]; static int handle = -1; static void pbe_buffer_alloc(void) { if (handle > 0) return; /* already-allocated */ unsigned int total_len = (B0_SIZE + B2_SIZE + B3_SIZE) * 2; handle = core_alloc("dsp_pbe_buffer",sizeof(int32_t) * total_len); if (handle < 0) { pbe_strength = 0; return; } memset(core_get_data(handle),0,sizeof(int32_t) * total_len); } static void pbe_buffer_get_data(void) { if (handle < 0) return; b0[0] = core_get_data(handle); b0[1] = b0[0] + B0_SIZE; b2[0] = b0[1] + B0_SIZE; b2[1] = b2[0] + B2_SIZE; b3[0] = b2[1] + B2_SIZE; b3[1] = b3[0] + B3_SIZE; } static void dsp_pbe_flush(void) { if (pbe_strength == 0) return; /* Not currently enabled */ unsigned int total_len = (B0_SIZE + B2_SIZE + B3_SIZE) * 2; if (handle > 0) memset(core_get_data(handle),0,sizeof(int32_t) * total_len); b0_r[0] = 0; b0_w[0] = 0; b0_r[1] = 0; b0_w[1] = 0; b2_r[0] = 0; b2_w[0] = 0; b2_r[1] = 0; b2_w[1] = 0; b3_r[0] = 0; b3_w[0] = 0; b3_r[1] = 0; b3_w[1] = 0; for (int i = 0; i < 5; i++) filter_flush(&pbe_filter[i]); } static void pbe_update_filter(unsigned int fout) { tcoef1 = fp_div(160, fout, 31); tcoef2 = fp_div(500, fout, 31); tcoef3 = fp_div(1150, fout, 31); /* Biophonic EQ */ filter_bishelf_coefs(fp_div(20, fout, 32), fp_div(16000, fout, 32), 0, 53, -5 + pbe_precut, &pbe_filter[0]); filter_pk_coefs(fp_div(64, fout, 32), 28, 53, &pbe_filter[1]); filter_pk_coefs(fp_div(2000, fout, 32), 28, 58, &pbe_filter[2]); filter_pk_coefs(fp_div(7500, fout, 32), 43, -82, &pbe_filter[3]); filter_pk_coefs(fp_div(10000, fout, 32), 43, -29, &pbe_filter[4]); } void dsp_pbe_precut(int var) { if (var == pbe_precut) return; /* No change */ pbe_precut = var; if (pbe_strength == 0) return; /* Not currently enabled */ /* Push more DSP_PROC_INIT messages to force filter updates (with value = 1) */ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); dsp_proc_enable(dsp, DSP_PROC_PBE, true); } void dsp_pbe_enable(int var) { if (var == pbe_strength) return; /* No change */ bool was_enabled = pbe_strength > 0; pbe_strength = var; bool now_enabled = var > 0; if (now_enabled == was_enabled) return; /* No change in enabled status */ if (now_enabled == false && handle > 0) { core_free(handle); handle = -1; } struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); dsp_proc_enable(dsp, DSP_PROC_PBE, now_enabled); } static void pbe_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p) { struct dsp_buffer *buf = *buf_p; int count = buf->remcount; int num_channels = buf->format.num_channels; int b2_level = (B2_DLY * pbe_strength) / 100; int b0_level = (B0_DLY * pbe_strength) / 100; int32_t x; pbe_buffer_get_data(); for(int ch = 0; ch < num_channels; ch++) { for (int i = 0; i < count; i++) { /* 160hz - 500hz no delay */ temp_buffer = FRACMUL(buf->p32[ch][i], tcoef1) - FRACMUL(buf->p32[ch][i], tcoef2); /* delay below 160hz*/ x = buf->p32[ch][i] - FRACMUL(buf->p32[ch][i], tcoef1); temp_buffer += dequeue(b0[ch], &b0_r[ch], b0_level); enqueue(x, b0[ch], &b0_w[ch], b0_level ); /* delay 500-1150hz */ x = FRACMUL(buf->p32[ch][i], tcoef2) - FRACMUL(buf->p32[ch][i], tcoef3); temp_buffer += dequeue(b2[ch], &b2_r[ch], b2_level); enqueue(x, b2[ch], &b2_w[ch], b2_level ); /* delay anything beyond 1150hz */ x = FRACMUL(buf->p32[ch][i], tcoef3); temp_buffer += dequeue(b3[ch], &b3_r[ch], B3_DLY); enqueue(x, b3[ch], &b3_w[ch], B3_DLY ); buf->p32[ch][i] = temp_buffer; } } /* apply Biophonic EQ */ for (int i = 0; i < 5; i++) filter_process(&pbe_filter[i], buf->p32, buf->remcount, buf->format.num_channels); (void)this; } /* DSP message hook */ static intptr_t pbe_configure(struct dsp_proc_entry *this, struct dsp_config *dsp, unsigned int setting, intptr_t value) { /* This only attaches to the audio (codec) DSP */ switch (setting) { case DSP_PROC_INIT: if (value == 0) { /* Coming online; was disabled */ this->process = pbe_process; pbe_buffer_alloc(); dsp_pbe_flush(); dsp_proc_activate(dsp, DSP_PROC_PBE, true); } /* else additional forced messages */ pbe_update_filter(dsp_get_output_frequency(dsp)); break; case DSP_FLUSH: /* Discontinuity; clear filters */ dsp_pbe_flush(); break; case DSP_SET_OUT_FREQUENCY: /* New output frequency */ pbe_update_filter(value); break; } return 1; } /* Database entry */ DSP_PROC_DB_ENTRY( PBE, pbe_configure);