summaryrefslogtreecommitdiff
path: root/lib/rbcodec/dsp/pbe.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/dsp/pbe.c')
-rw-r--r--lib/rbcodec/dsp/pbe.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/lib/rbcodec/dsp/pbe.c b/lib/rbcodec/dsp/pbe.c
new file mode 100644
index 0000000000..49665880c0
--- /dev/null
+++ b/lib/rbcodec/dsp/pbe.c
@@ -0,0 +1,251 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 */
+
+ pbe_buffer_get_data();
+
+ memset(b0[0], 0, B0_DLY * sizeof(int32_t));
+ memset(b0[1], 0, B0_DLY * sizeof(int32_t));
+ memset(b2[0], 0, B2_DLY * sizeof(int32_t));
+ memset(b2[1], 0, B2_DLY * sizeof(int32_t));
+ memset(b3[0], 0, B3_DLY * sizeof(int32_t));
+ memset(b3[1], 0, B3_DLY * sizeof(int32_t));
+ b0_r[0] = 0; b0_w[0] = B0_DLY;
+ b0_r[1] = 0; b0_w[1] = B0_DLY;
+ b2_r[0] = 0; b2_w[0] = B2_DLY;
+ b2_r[1] = 0; b2_w[1] = B2_DLY;
+ b3_r[0] = 0; b3_w[0] = B3_DLY;
+ b3_r[1] = 0; b3_w[1] = B3_DLY;
+
+ 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);