diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 113570522c..ca5e687ea8 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -13254,3 +13254,122 @@
swcodec: "Q"
+
+ id: LANG_PBE
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "Perceptual Bass Enhancement"
+
+
+ *: none
+ swcodec: "Perceptual Bass Enhancement"
+
+
+
+ id: LANG_AFR
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "Auditory Fatigue Reduction"
+
+
+ *: none
+ swcodec: "Auditory Fatigue Reduction"
+
+
+
+ id: LANG_SURROUND
+ desc: in the sound settings menu
+ user: core
+
+
+ *: none
+ swcodec: "Haas Surround"
+
+
+ *: none
+ swcodec: "Haas Surround"
+
+
+
+ id: LANG_SURROUND_FX1
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "f(x1)"
+
+
+ *: none
+ swcodec: "f(x1)"
+
+
+
+ id: LANG_SURROUND_FX2
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "f(x2)"
+
+
+ *: none
+ swcodec: "f(x2)"
+
+
+
+ id: LANG_SURROUND_METHOD2
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "SIDE ONLY"
+
+
+ *: none
+ swcodec: "SIDE ONLY"
+
+
+
+ id: LANG_SURROUND_MIX
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "Dry / Wet Mix"
+
+
+ *: none
+ swcodec: "Dry / Wet Mix"
+
+
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 8913266a5f..7c04662233 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -166,6 +166,28 @@ static int timestretch_callback(int action,const struct menu_item_ex *this_item)
MENUITEM_SETTING(dithering_enabled,
&global_settings.dithering_enabled, lowlatency_callback);
+ MENUITEM_SETTING(afr_enabled,
+ &global_settings.afr_enabled, lowlatency_callback);
+ MENUITEM_SETTING(pbe,
+ &global_settings.pbe, lowlatency_callback);
+ MENUITEM_SETTING(pbe_precut,
+ &global_settings.pbe_precut, lowlatency_callback);
+ MAKE_MENU(pbe_menu,ID2P(LANG_PBE), NULL, Icon_NOICON,
+ &pbe,&pbe_precut);
+ MENUITEM_SETTING(surround_enabled,
+ &global_settings.surround_enabled, lowlatency_callback);
+ MENUITEM_SETTING(surround_balance,
+ &global_settings.surround_balance, lowlatency_callback);
+ MENUITEM_SETTING(surround_fx1,
+ &global_settings.surround_fx1, lowlatency_callback);
+ MENUITEM_SETTING(surround_fx2,
+ &global_settings.surround_fx2, lowlatency_callback);
+ MENUITEM_SETTING(surround_method2,
+ &global_settings.surround_method2, lowlatency_callback);
+ MENUITEM_SETTING(surround_mix,
+ &global_settings.surround_mix, lowlatency_callback);
+ MAKE_MENU(surround_menu,ID2P(LANG_SURROUND), NULL, Icon_NOICON,
+ &surround_enabled,&surround_balance,&surround_fx1,&surround_fx2,&surround_method2,&surround_mix);
/* compressor submenu */
MENUITEM_SETTING(compressor_threshold,
@@ -236,6 +258,7 @@ MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, Icon_Audio,
#endif
#if CONFIG_CODEC == SWCODEC
,&crossfeed_menu, &equalizer_menu, &dithering_enabled
+ ,&surround_menu, &pbe_menu, &afr_enabled
#ifdef HAVE_PITCHCONTROL
,×tretch_enabled
#endif
diff --git a/apps/settings.c b/apps/settings.c
index 16d572eb0d..f144a4af03 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -1034,6 +1034,13 @@ void settings_apply(bool read_disk)
}
dsp_dither_enable(global_settings.dithering_enabled);
+ dsp_surround_set_balance(global_settings.surround_balance);
+ dsp_surround_set_cutoff(global_settings.surround_fx1, global_settings.surround_fx2);
+ dsp_surround_mix(global_settings.surround_mix);
+ dsp_surround_enable(global_settings.surround_enabled);
+ dsp_afr_enable(global_settings.afr_enabled);
+ dsp_pbe_precut(global_settings.pbe_precut);
+ dsp_pbe_enable(global_settings.pbe);
#ifdef HAVE_PITCHCONTROL
dsp_timestretch_enable(global_settings.timestretch_enabled);
#endif
diff --git a/apps/settings.h b/apps/settings.h
index 3ec5ee6207..3029026ab6 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -838,6 +838,18 @@ struct user_settings
int play_frequency; /* core audio output frequency selection */
#endif
int volume_limit; /* maximum volume limit */
+
+ int surround_enabled;
+ int surround_balance;
+ int surround_fx1;
+ int surround_fx2;
+ bool surround_method2;
+ int surround_mix;
+
+ int pbe;
+ int pbe_precut;
+
+ int afr_enabled;
};
/** global variables **/
diff --git a/apps/settings_list.c b/apps/settings_list.c
index cddc062d40..abdb512a42 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -446,6 +446,12 @@ static void crossfeed_cross_set(int val)
global_settings.crossfeed_hf_cutoff);
}
+static void surround_set_factor(int val)
+{
+ (void)val;
+ dsp_surround_set_cutoff(global_settings.surround_fx1, global_settings.surround_fx2);
+}
+
static void compressor_set(int val)
{
(void)val;
@@ -1667,7 +1673,44 @@ const struct settings_list settings[] = {
/* dithering */
OFFON_SETTING(F_SOUNDSETTING, dithering_enabled, LANG_DITHERING, false,
"dithering enabled", dsp_dither_enable),
-
+ /* surround */
+ TABLE_SETTING(F_SOUNDSETTING, surround_enabled,
+ LANG_SURROUND, 0, "surround enabled", "off",
+ UNIT_MS, formatter_unit_0_is_off, getlang_unit_0_is_off,
+ dsp_surround_enable, 6,
+ 0,5,8,10,15,30),
+ INT_SETTING_NOWRAP(F_SOUNDSETTING, surround_balance,
+ LANG_BALANCE, 35,
+ "surround balance", UNIT_PERCENT, 0, 99,
+ 1, NULL, NULL, dsp_surround_set_balance),
+ INT_SETTING_NOWRAP(F_SOUNDSETTING, surround_fx1,
+ LANG_SURROUND_FX1, 3400,
+ "surround_fx1", UNIT_HERTZ, 600, 8000,
+ 200, NULL, NULL, surround_set_factor),
+ INT_SETTING_NOWRAP(F_SOUNDSETTING, surround_fx2,
+ LANG_SURROUND_FX2, 320,
+ "surround_fx2", UNIT_HERTZ, 40, 400,
+ 40, NULL, NULL, surround_set_factor),
+ OFFON_SETTING(F_SOUNDSETTING, surround_method2, LANG_SURROUND_METHOD2, false,
+ "side only", dsp_surround_side_only),
+ INT_SETTING_NOWRAP(F_SOUNDSETTING, surround_mix,
+ LANG_SURROUND_MIX, 50,
+ "surround mix", UNIT_PERCENT, 0, 100,
+ 5, NULL, NULL, dsp_surround_mix),
+ /* auditory fatigue reduction */
+ CHOICE_SETTING(F_SOUNDSETTING|F_NO_WRAP, afr_enabled,
+ LANG_AFR, 0,"afr enabled",
+ "off,weak,moderate,strong", dsp_afr_enable, 4,
+ ID2P(LANG_OFF), ID2P(LANG_WEAK),ID2P(LANG_MODERATE),ID2P(LANG_STRONG)),
+ /* PBE */
+ INT_SETTING_NOWRAP(F_SOUNDSETTING, pbe,
+ LANG_PBE, 0,
+ "pbe", UNIT_PERCENT, 0, 100,
+ 25, NULL, NULL, dsp_pbe_enable),
+ INT_SETTING_NOWRAP(F_SOUNDSETTING, pbe_precut,
+ LANG_EQUALIZER_PRECUT, -25,
+ "pbe precut", UNIT_DB, -45, 0,
+ 1, db_format, NULL, dsp_pbe_precut),
#ifdef HAVE_PITCHCONTROL
/* timestretch */
OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false,
diff --git a/lib/rbcodec/SOURCES b/lib/rbcodec/SOURCES
index 401d546ee6..c2a32e9c28 100644
--- a/lib/rbcodec/SOURCES
+++ b/lib/rbcodec/SOURCES
@@ -7,6 +7,9 @@ dsp/channel_mode.c
dsp/compressor.c
dsp/crossfeed.c
dsp/dsp_core.c
+dsp/pbe.c
+dsp/afr.c
+dsp/surround.c
dsp/dsp_filter.c
dsp/dsp_misc.c
dsp/dsp_sample_input.c
diff --git a/lib/rbcodec/dsp/afr.c b/lib/rbcodec/dsp/afr.c
new file mode 100644
index 0000000000..b266e4b549
--- /dev/null
+++ b/lib/rbcodec/dsp/afr.c
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "afr.h"
+#include "config.h"
+#include "fixedpoint.h"
+#include "fracmul.h"
+#include "settings.h"
+#include "dsp_proc_entry.h"
+#include "dsp_filter.h"
+
+/* Auditory fatigue reduction */
+
+static int afr_strength = 0;
+static struct dsp_filter afr_filters[4];
+
+static void dsp_afr_flush(void)
+{
+ for (int i = 0 ;i < 4;i++)
+ filter_flush(&afr_filters[i]);
+}
+
+static void strength_update(unsigned int fout)
+{
+ int hs=0, ls=0, drop3k=0;
+
+ switch (afr_strength)
+ {
+ /* not called if afr_strength == 0 (disabled) */
+ case 1:
+ hs=-29; ls=0; drop3k=-13;
+ break;
+ case 2:
+ hs=-58; ls=-5; drop3k=-29;
+ break;
+ case 3:
+ hs=-72; ls=-5; drop3k=-48;
+ break;
+ }
+
+ filter_bishelf_coefs(fp_div(8000, fout, 32),
+ fp_div(20000, fout, 32),
+ 0, (hs/2), 0,
+ &afr_filters[0]);
+
+ filter_pk_coefs(fp_div(8000, fout, 32), 28, hs,
+ &afr_filters[1]);
+
+ filter_pk_coefs(fp_div(3150, fout, 32), 28, drop3k,
+ &afr_filters[2]);
+
+ filter_bishelf_coefs(fp_div(200, fout, 32),
+ fp_div(1280, fout, 32),
+ ls, 0, 0,
+ &afr_filters[3]);
+}
+
+void dsp_afr_enable(int var)
+{
+ if (var == afr_strength)
+ return; /* No setting change */
+
+ bool was_enabled = afr_strength > 0;
+ afr_strength = var;
+
+ bool now_enabled = var > 0;
+
+ if (was_enabled == now_enabled && !now_enabled)
+ return;
+
+ /* If changing status, enable or disable it; if already enabled push
+ additional DSP_PROC_INIT messages with value = 1 to force-update the
+ filters */
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ dsp_proc_enable(dsp, DSP_PROC_AFR, now_enabled);
+}
+
+static void afr_reduce_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *buf = *buf_p;
+
+ for (int i = 0; i < 4; i++)
+ filter_process(&afr_filters[i], buf->p32, buf->remcount,
+ buf->format.num_channels);
+
+ (void)this;
+}
+
+/* DSP message hook */
+static intptr_t afr_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 = afr_reduce_process;
+ dsp_afr_flush();
+ dsp_proc_activate(dsp, DSP_PROC_AFR, true);
+ }
+ /* else additional forced messages */
+
+ strength_update(dsp_get_output_frequency(dsp));
+ break;
+
+ case DSP_FLUSH:
+ /* Discontinuity; clear filters */
+ dsp_afr_flush();
+ break;
+
+ case DSP_SET_OUT_FREQUENCY:
+ /* New output frequency */
+ strength_update(value);
+ break;
+ }
+
+ return 1;
+}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(
+ AFR,
+ afr_configure);
diff --git a/lib/rbcodec/dsp/afr.h b/lib/rbcodec/dsp/afr.h
new file mode 100644
index 0000000000..783d7e1760
--- /dev/null
+++ b/lib/rbcodec/dsp/afr.h
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef AFR_H
+#define AFR_H
+
+#include
+void dsp_afr_enable(int var);
+
+#endif /* AFR_H */
diff --git a/lib/rbcodec/dsp/dsp_filter.c b/lib/rbcodec/dsp/dsp_filter.c
index b74d499e51..3656026536 100644
--- a/lib/rbcodec/dsp/dsp_filter.c
+++ b/lib/rbcodec/dsp/dsp_filter.c
@@ -72,7 +72,7 @@ void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c)
*c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */
}
-#ifdef HAVE_SW_TONE_CONTROLS
+
/**
* Calculate second order section filter consisting of one low-shelf and one
* high-shelf section.
@@ -114,7 +114,7 @@ void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
f->shift = FILTER_BISHELF_SHIFT;
}
-#endif /* HAVE_SW_TONE_CONTROLS */
+
/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
* Slightly faster calculation can be done by deriving forms which use tan()
@@ -303,3 +303,23 @@ void filter_process(struct dsp_filter *f, int32_t * const buf[], int count,
}
}
#endif /* CPU */
+
+/* ring buffer */
+int32_t dequeue(int32_t* buffer, int *head, int boundary)
+{
+ int32_t var = buffer[*head];
+ if (*head +1 >= boundary)
+ *head = 0;
+ else
+ *head += 1;
+ return var;
+}
+
+void enqueue(int32_t var, int32_t* buffer, int *head, int boundary)
+{
+ buffer[*head] = var;
+ if (*head +1 >= boundary)
+ *head = 0;
+ else
+ *head += 1;
+}
diff --git a/lib/rbcodec/dsp/dsp_filter.h b/lib/rbcodec/dsp/dsp_filter.h
index af6e20ce86..ec922389c9 100644
--- a/lib/rbcodec/dsp/dsp_filter.h
+++ b/lib/rbcodec/dsp/dsp_filter.h
@@ -37,12 +37,10 @@ struct dsp_filter
};
void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c);
-#ifdef HAVE_SW_TONE_CONTROLS
void filter_bishelf_coefs(unsigned long cutoff_low,
unsigned long cutoff_high,
long A_low, long A_high, long A,
struct dsp_filter *f);
-#endif /* HAVE_SW_TONE_CONTROLS */
void filter_pk_coefs(unsigned long cutoff, unsigned long Q, long db,
struct dsp_filter *f);
void filter_ls_coefs(unsigned long cutoff, unsigned long Q, long db,
@@ -53,5 +51,7 @@ void filter_copy(struct dsp_filter *dst, const struct dsp_filter *src);
void filter_flush(struct dsp_filter *f);
void filter_process(struct dsp_filter *f, int32_t * const buf[], int count,
unsigned int channels);
-
+/* ring buffer */
+void enqueue(int32_t var, int32_t* buffer, int *head, int boundary);
+int32_t dequeue(int32_t* buffer, int *head, int boundary);
#endif /* DSP_FILTER_H */
diff --git a/lib/rbcodec/dsp/dsp_proc_database.h b/lib/rbcodec/dsp/dsp_proc_database.h
index 534c165eef..f30ef0b5c0 100644
--- a/lib/rbcodec/dsp/dsp_proc_database.h
+++ b/lib/rbcodec/dsp/dsp_proc_database.h
@@ -46,6 +46,9 @@ DSP_PROC_DB_START
#ifdef HAVE_SW_TONE_CONTROLS
DSP_PROC_DB_ITEM(TONE_CONTROLS) /* bass and treble */
#endif
+ DSP_PROC_DB_ITEM(PBE) /* perceptual bass enhancement */
+ DSP_PROC_DB_ITEM(AFR) /* auditory fatigue reduction */
+ DSP_PROC_DB_ITEM(SURROUND) /* haas surround */
DSP_PROC_DB_ITEM(CHANNEL_MODE) /* channel modes */
DSP_PROC_DB_ITEM(COMPRESSOR) /* dynamic-range compressor */
DSP_PROC_DB_STOP
diff --git a/lib/rbcodec/dsp/dsp_proc_settings.h b/lib/rbcodec/dsp/dsp_proc_settings.h
index 26f603b8b1..8f6e3efab4 100644
--- a/lib/rbcodec/dsp/dsp_proc_settings.h
+++ b/lib/rbcodec/dsp/dsp_proc_settings.h
@@ -28,6 +28,9 @@
#include "dsp_misc.h"
#include "eq.h"
#include "pga.h"
+#include "surround.h"
+#include "afr.h"
+#include "pbe.h"
#ifdef HAVE_PITCHCONTROL
#include "tdspeed.h"
#endif
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);
diff --git a/lib/rbcodec/dsp/pbe.h b/lib/rbcodec/dsp/pbe.h
new file mode 100644
index 0000000000..1bcf533a63
--- /dev/null
+++ b/lib/rbcodec/dsp/pbe.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PBE_H
+#define PBE_H
+
+#include
+void dsp_pbe_enable(int var);
+void dsp_pbe_precut(int var);
+
+#endif /* PBE_H */
+
diff --git a/lib/rbcodec/dsp/surround.c b/lib/rbcodec/dsp/surround.c
new file mode 100644
index 0000000000..f420bf4cf5
--- /dev/null
+++ b/lib/rbcodec/dsp/surround.c
@@ -0,0 +1,333 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "surround.h"
+#include "config.h"
+#include "fixedpoint.h"
+#include "fracmul.h"
+#include "settings.h"
+#include "dsp_proc_entry.h"
+#include "dsp_filter.h"
+#include "core_alloc.h"
+
+static bool surround_enabled = false;
+static int surround_balance = 0;
+static bool surround_side_only = false;
+static int surround_mix = 100;
+static int surround_strength = 0;
+/*1 sample ~ 11ns */
+#define DLY_5MS 454
+#define DLY_8MS 727
+#define DLY_10MS 909
+#define DLY_15MS 1363
+#define DLY_30MS 2727
+#define MAX_DLY DLY_30MS
+
+#define B0_DLY (MAX_DLY/8 + 1)
+#define B2_DLY (MAX_DLY + 1)
+#define BB_DLY (MAX_DLY/4 + 1)
+#define HH_DLY (MAX_DLY/2 + 1)
+#define CL_DLY B2_DLY
+/*only need to buffer right channel */
+static int32_t *b0, *b2, *bb, *hh, *cl;
+static int32_t temp_buffer[2];
+static int32_t mid, side;
+
+/*voice from 300hz - 3400hz ?*/
+static int32_t tcoef1,tcoef2,bcoef,hcoef;
+
+static int dly_size = MAX_DLY;
+static int cutoff_l = 320;
+static int cutoff_h = 3400;
+
+static int b0_r=0,b0_w=0,
+ b2_r=0,b2_w=0,
+ bb_r=0,bb_w=0,
+ hh_r=0,hh_w=0,
+ cl_r=0,cl_w=0;
+static int handle = -1;
+
+static void surround_buffer_alloc(void)
+{
+ if (handle > 0)
+ return; /* already-allocated */
+
+ unsigned int total_len = B0_DLY + B2_DLY + BB_DLY + HH_DLY + CL_DLY;
+ handle = core_alloc("dsp_surround_buffer",sizeof(int32_t) * total_len);
+
+ if (handle < 0)
+ {
+ surround_enabled = false;
+ return;
+ }
+ memset(core_get_data(handle),0,sizeof(int32_t) * total_len);
+}
+
+static void surround_buffer_get_data(void)
+{
+ if (handle < 0)
+ return;
+ b0 = core_get_data(handle);
+ b2 = b0 + B0_DLY;
+ bb = b2 + B2_DLY;
+ hh = bb + BB_DLY;
+ cl = hh + HH_DLY;
+}
+
+static void dsp_surround_flush(void)
+{
+ if (!surround_enabled)
+ return;
+
+ surround_buffer_get_data();
+
+ memset(b0,0,MAX_DLY/8 * sizeof(int32_t));
+ memset(b2,0,MAX_DLY * sizeof(int32_t));
+ memset(bb,0,MAX_DLY/4 * sizeof(int32_t));
+ memset(hh,0,MAX_DLY/2 * sizeof(int32_t));
+ memset(cl,0,MAX_DLY * sizeof(int32_t));
+ b0_r = 0;b0_w = dly_size/8 - 1;
+ b2_r = 0;b2_w = dly_size - 1;
+ bb_r = 0;bb_w = dly_size/4 - 1;
+ hh_r = 0;hh_w = dly_size/2 - 1;
+ cl_r = 0;cl_w = dly_size - 1;
+}
+
+static void surround_update_filter(unsigned int fout)
+{
+ tcoef1 = fp_div(cutoff_l, fout, 31);
+ tcoef2 = fp_div(cutoff_h, fout, 31);
+ bcoef = fp_div(cutoff_l / 2, fout, 31);
+ hcoef = fp_div(cutoff_h * 2, fout, 31);
+}
+
+void dsp_surround_set_balance(int var)
+{
+ surround_balance = var;
+}
+
+void dsp_surround_side_only(bool var)
+{
+ dsp_surround_flush();
+ surround_side_only = var;
+}
+
+void dsp_surround_mix(int var)
+{
+ surround_mix = var;
+}
+
+void dsp_surround_set_cutoff(int frq_l, int frq_h)
+{
+ cutoff_l = frq_l;/*fx2*/
+ cutoff_h = frq_h;/*fx1*/
+
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ unsigned int fout = dsp_get_output_frequency(dsp);
+ surround_update_filter(fout);
+}
+
+static void surround_set_stepsize(int surround_strength)
+{
+ dsp_surround_flush();
+ switch(surround_strength)
+ {
+ case 1:
+ dly_size = DLY_5MS;
+ break;
+ case 2:
+ dly_size = DLY_8MS;
+ break;
+ case 3:
+ dly_size = DLY_10MS;
+ break;
+ case 4:
+ dly_size = DLY_15MS;
+ break;
+ case 5:
+ dly_size = DLY_30MS;
+ break;
+ }
+}
+
+void dsp_surround_enable(int var)
+{
+ if (var == surround_strength)
+ return; /* No setting change */
+
+ bool was_enabled = surround_strength > 0;
+ surround_strength = var;
+ surround_set_stepsize(surround_strength);
+
+ bool now_enabled = var > 0;
+
+ if (was_enabled == now_enabled && !now_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_SURROUND, now_enabled);
+}
+
+static void surround_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+
+ struct dsp_buffer *buf = *buf_p;
+ int count = buf->remcount;
+
+ int dly_shift3 = dly_size/8;
+ int dly_shift2 = dly_size/4;
+ int dly_shift1 = dly_size/2;
+ int dly = dly_size;
+ int i;
+ int32_t x;
+
+ surround_buffer_get_data();
+
+ for (i = 0; i < count; i++)
+ {
+ mid = buf->p32[0][i] /2 + buf->p32[1][i] /2;
+ side = buf->p32[0][i] - buf->p32[1][i];
+
+ if (!surround_side_only)
+ {
+ /*clone the left channal*/
+ temp_buffer[0]= buf->p32[0][i];
+ /*keep the middle band of right channel*/
+ temp_buffer[1]= FRACMUL(buf->p32[1][i], tcoef1) -
+ FRACMUL(buf->p32[1][i], tcoef2);
+ }
+ else /* apply haas to side only*/
+ {
+ temp_buffer[0] = side / 2;
+ temp_buffer[1] = FRACMUL(-side,tcoef1)/2 -
+ FRACMUL(-side, tcoef2)/2;
+ }
+
+ /* inverted crossfeed delay (left channel) to make sound wider*/
+ x = temp_buffer[1]/100 * 35;
+ temp_buffer[0] += dequeue(cl, &cl_r, dly);
+ enqueue(-x, cl, &cl_w, dly);
+
+ /* apply 1/8 delay to frequency below fx2 */
+ x = buf->p32[1][i] - FRACMUL(buf->p32[1][i], tcoef1);
+ temp_buffer[1] += dequeue(b0, &b0_r, dly_shift3);
+ enqueue(x, b0, &b0_w, dly_shift3 );
+
+ /* cut frequency below half fx2*/
+ temp_buffer[1] = FRACMUL(temp_buffer[1], bcoef);
+
+ /* apply 1/4 delay to frequency below half fx2 */
+ /* use different delay to fake the sound direction*/
+ x = buf->p32[1][i] - FRACMUL(buf->p32[1][i], bcoef);
+ temp_buffer[1] += dequeue(bb, &bb_r, dly_shift2);
+ enqueue(x, bb, &bb_w, dly_shift2 );
+
+ /* apply full delay to higher band */
+ x = FRACMUL(buf->p32[1][i], tcoef2);
+ temp_buffer[1] += dequeue(b2, &b2_r, dly);
+ enqueue(x, b2, &b2_w, dly );
+
+ /* do the same direction trick again */
+ temp_buffer[1] -= FRACMUL(temp_buffer[1], hcoef);
+
+ x = FRACMUL(buf->p32[1][i], hcoef);
+ temp_buffer[1] += dequeue(hh, &hh_r, dly_shift1);
+ enqueue(x, hh, &hh_w, dly_shift1 );
+ /*balance*/
+ if (surround_balance > 0 && !surround_side_only)
+ {
+ temp_buffer[0] -= temp_buffer[0]/200 * surround_balance;
+ temp_buffer[1] += temp_buffer[1]/200 * surround_balance;
+ }
+ else if (surround_balance > 0)
+ {
+ temp_buffer[0] += temp_buffer[0]/200 * surround_balance;
+ temp_buffer[1] -= temp_buffer[1]/200 * surround_balance;
+ }
+
+ if (surround_side_only)
+ {
+ temp_buffer[0] += mid;
+ temp_buffer[1] += mid;
+ }
+
+ if (surround_mix == 100)
+ {
+ buf->p32[0][i] = temp_buffer[0];
+ buf->p32[1][i] = temp_buffer[1];
+ }
+ else
+ {
+ /*dry wet mix*/
+ buf->p32[0][i] = buf->p32[0][i]/100 * (100-surround_mix) +
+ temp_buffer[0]/100 * surround_mix;
+ buf->p32[1][i] = buf->p32[1][i]/100 * (100-surround_mix) +
+ temp_buffer[1]/100 * surround_mix;
+ }
+ }
+ (void)this;
+}
+
+/* DSP message hook */
+static intptr_t surround_configure(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned int setting,
+ intptr_t value)
+{
+ unsigned int fout = dsp_get_output_frequency(dsp);
+ switch (setting)
+ {
+ case DSP_PROC_INIT:
+ if (value == 0)
+ {
+ this->process = surround_process;
+ surround_buffer_alloc();
+ dsp_surround_flush();
+ dsp_proc_activate(dsp, DSP_PROC_SURROUND, true);
+ }
+ else
+ surround_update_filter(fout);
+ break;
+ case DSP_FLUSH:
+ dsp_surround_flush();
+ break;
+ case DSP_SET_OUT_FREQUENCY:
+ surround_update_filter(value);
+ break;
+ case DSP_PROC_CLOSE:
+ break;
+ }
+
+ return 1;
+ (void)dsp;
+}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(
+ SURROUND,
+ surround_configure);
+
diff --git a/lib/rbcodec/dsp/surround.h b/lib/rbcodec/dsp/surround.h
new file mode 100644
index 0000000000..1683367865
--- /dev/null
+++ b/lib/rbcodec/dsp/surround.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef SURROUND_H
+#define SURROUND_H
+
+#include
+void dsp_surround_enable(int var);
+void dsp_surround_set_balance(int var);
+void dsp_surround_set_cutoff(int frq_l, int frq_h);
+void dsp_surround_side_only(bool var);
+void dsp_surround_mix(int var);
+
+#endif /* SURROUND_H */
diff --git a/manual/configure_rockbox/sound_settings.tex b/manual/configure_rockbox/sound_settings.tex
index 8feacd96cc..3fe7efbade 100644
--- a/manual/configure_rockbox/sound_settings.tex
+++ b/manual/configure_rockbox/sound_settings.tex
@@ -590,6 +590,33 @@ details about how to use the feature.
}
}
+\opt{swcodec}{
+\section{Haas Surround}
+This setting does Haas effect with adjustable delay time to enhance the stereo stage
+of the sound. A full range haas effect may sounds like everything starts from left channel ends at right.
+Therefore, four additional functions are provided to move the stage back to the center:
+First is a \setting{Balance} setting for change left-right channel output ratio. Second
+is a bypass band for frequency mostly contain vocals, using \setting{f(x1)}, \setting{f(x2)} to reserved the
+frequncy range. The \setting{SIDE ONLY} setting uses mid-side
+processing to determine and apply effect to side only. And finally, a \setting{Dry/Wet Mix}
+to adjust the proportion of mix between the original (dry) and 'effected' (wet) signals.
+}
+
+\opt{swcodec}{
+\section{Perceptual Bass Enhancement}
+This setting dose a group delay correction and an additional Biophonic EQ to
+emphasize the clarity of the sounds. The \setting{Precut} setting provides
+negative overall gain to prevent possible audio distortion due to the EQ
+gain. The defult precut value is set to -2.5 dB and can be adjust from 0 dB to -4.5 dB.
+}
+
+\opt{swcodec}{
+\section{Auditory Fatigue Reduction}
+Human hearing are most senstive to some frequency bands, and this setting applies custom presets
+of equalization and bi-shelf filter to reduce signals in those bands to minimize
+the chance that temporary threshold shift (Auditory Fatigue) occurs.
+}
+
\opt{swcodec}{
\section{Compressor}
The \setting{Compressor} reduces, or compresses, the dynamic range of the audio