three new DSPs

perceptual bass enhancement
- a bbe-ish group delay corrction with Biophonic EQ boost.
- precut

auditory fatigue reduction
-reduce signal in frequency that may trigger temporary threshold shift

haas surround
-frequency between f(x1) and f(x2) is always bypassed.
-can apply to side only.

Change-Id: Icb6355ce9b1c99bf2c58c9385c3c411c0ae209d3
This commit is contained in:
Chiwen Chang 2014-08-22 23:30:35 +10:00 committed by Gerrit Rockbox
parent 2cdfc43f10
commit 3ae0f32ac3
17 changed files with 1084 additions and 6 deletions

View file

@ -13254,3 +13254,122 @@
swcodec: "Q" swcodec: "Q"
</voice> </voice>
</phrase> </phrase>
<phrase>
id: LANG_PBE
desc: in sound settings
user: core
<source>
*: none
swcodec: "Perceptual Bass Enhancement"
</source>
<dest>
*: none
swcodec: "Perceptual Bass Enhancement"
</dest>
<voice>
*: none
swcodec: "Perceptual Bass Enhancement"
</voice>
</phrase>
<phrase>
id: LANG_AFR
desc: in sound settings
user: core
<source>
*: none
swcodec: "Auditory Fatigue Reduction"
</source>
<dest>
*: none
swcodec: "Auditory Fatigue Reduction"
</dest>
<voice>
*: none
swcodec: "Auditory Fatigue Reduction"
</voice>
</phrase>
<phrase>
id: LANG_SURROUND
desc: in the sound settings menu
user: core
<source>
*: none
swcodec: "Haas Surround"
</source>
<dest>
*: none
swcodec: "Haas Surround"
</dest>
<voice>
*: none
swcodec: "Haas Surround"
</voice>
</phrase>
<phrase>
id: LANG_SURROUND_FX1
desc: in sound settings
user: core
<source>
*: none
swcodec: "f(x1)"
</source>
<dest>
*: none
swcodec: "f(x1)"
</dest>
<voice>
*: none
swcodec: "f(x1)"
</voice>
</phrase>
<phrase>
id: LANG_SURROUND_FX2
desc: in sound settings
user: core
<source>
*: none
swcodec: "f(x2)"
</source>
<dest>
*: none
swcodec: "f(x2)"
</dest>
<voice>
*: none
swcodec: "f(x2)"
</voice>
</phrase>
<phrase>
id: LANG_SURROUND_METHOD2
desc: in sound settings
user: core
<source>
*: none
swcodec: "SIDE ONLY"
</source>
<dest>
*: none
swcodec: "SIDE ONLY"
</dest>
<voice>
*: none
swcodec: "SIDE ONLY"
</voice>
</phrase>
<phrase>
id: LANG_SURROUND_MIX
desc: in sound settings
user: core
<source>
*: none
swcodec: "Dry / Wet Mix"
</source>
<dest>
*: none
swcodec: "Dry / Wet Mix"
</dest>
<voice>
*: none
swcodec: "Dry / Wet Mix"
</voice>
</phrase>

View file

@ -166,6 +166,28 @@ static int timestretch_callback(int action,const struct menu_item_ex *this_item)
MENUITEM_SETTING(dithering_enabled, MENUITEM_SETTING(dithering_enabled,
&global_settings.dithering_enabled, lowlatency_callback); &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 */ /* compressor submenu */
MENUITEM_SETTING(compressor_threshold, MENUITEM_SETTING(compressor_threshold,
@ -236,6 +258,7 @@ MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, Icon_Audio,
#endif #endif
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
,&crossfeed_menu, &equalizer_menu, &dithering_enabled ,&crossfeed_menu, &equalizer_menu, &dithering_enabled
,&surround_menu, &pbe_menu, &afr_enabled
#ifdef HAVE_PITCHCONTROL #ifdef HAVE_PITCHCONTROL
,&timestretch_enabled ,&timestretch_enabled
#endif #endif

View file

@ -1034,6 +1034,13 @@ void settings_apply(bool read_disk)
} }
dsp_dither_enable(global_settings.dithering_enabled); 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 #ifdef HAVE_PITCHCONTROL
dsp_timestretch_enable(global_settings.timestretch_enabled); dsp_timestretch_enable(global_settings.timestretch_enabled);
#endif #endif

View file

@ -838,6 +838,18 @@ struct user_settings
int play_frequency; /* core audio output frequency selection */ int play_frequency; /* core audio output frequency selection */
#endif #endif
int volume_limit; /* maximum volume limit */ 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 **/ /** global variables **/

View file

@ -446,6 +446,12 @@ static void crossfeed_cross_set(int val)
global_settings.crossfeed_hf_cutoff); 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) static void compressor_set(int val)
{ {
(void)val; (void)val;
@ -1667,7 +1673,44 @@ const struct settings_list settings[] = {
/* dithering */ /* dithering */
OFFON_SETTING(F_SOUNDSETTING, dithering_enabled, LANG_DITHERING, false, OFFON_SETTING(F_SOUNDSETTING, dithering_enabled, LANG_DITHERING, false,
"dithering enabled", dsp_dither_enable), "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 #ifdef HAVE_PITCHCONTROL
/* timestretch */ /* timestretch */
OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false, OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false,

View file

@ -7,6 +7,9 @@ dsp/channel_mode.c
dsp/compressor.c dsp/compressor.c
dsp/crossfeed.c dsp/crossfeed.c
dsp/dsp_core.c dsp/dsp_core.c
dsp/pbe.c
dsp/afr.c
dsp/surround.c
dsp/dsp_filter.c dsp/dsp_filter.c
dsp/dsp_misc.c dsp/dsp_misc.c
dsp/dsp_sample_input.c dsp/dsp_sample_input.c

147
lib/rbcodec/dsp/afr.c Normal file
View file

@ -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);

27
lib/rbcodec/dsp/afr.h Normal file
View file

@ -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 <stdbool.h>
void dsp_afr_enable(int var);
#endif /* AFR_H */

View file

@ -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 */ *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 * Calculate second order section filter consisting of one low-shelf and one
* high-shelf section. * high-shelf section.
@ -114,7 +114,7 @@ void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
f->shift = FILTER_BISHELF_SHIFT; f->shift = FILTER_BISHELF_SHIFT;
} }
#endif /* HAVE_SW_TONE_CONTROLS */
/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson. /* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
* Slightly faster calculation can be done by deriving forms which use tan() * 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 */ #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;
}

View file

@ -37,12 +37,10 @@ struct dsp_filter
}; };
void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c); 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, void filter_bishelf_coefs(unsigned long cutoff_low,
unsigned long cutoff_high, unsigned long cutoff_high,
long A_low, long A_high, long A, long A_low, long A_high, long A,
struct dsp_filter *f); struct dsp_filter *f);
#endif /* HAVE_SW_TONE_CONTROLS */
void filter_pk_coefs(unsigned long cutoff, unsigned long Q, long db, void filter_pk_coefs(unsigned long cutoff, unsigned long Q, long db,
struct dsp_filter *f); struct dsp_filter *f);
void filter_ls_coefs(unsigned long cutoff, unsigned long Q, long db, 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_flush(struct dsp_filter *f);
void filter_process(struct dsp_filter *f, int32_t * const buf[], int count, void filter_process(struct dsp_filter *f, int32_t * const buf[], int count,
unsigned int channels); 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 */ #endif /* DSP_FILTER_H */

View file

@ -46,6 +46,9 @@ DSP_PROC_DB_START
#ifdef HAVE_SW_TONE_CONTROLS #ifdef HAVE_SW_TONE_CONTROLS
DSP_PROC_DB_ITEM(TONE_CONTROLS) /* bass and treble */ DSP_PROC_DB_ITEM(TONE_CONTROLS) /* bass and treble */
#endif #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(CHANNEL_MODE) /* channel modes */
DSP_PROC_DB_ITEM(COMPRESSOR) /* dynamic-range compressor */ DSP_PROC_DB_ITEM(COMPRESSOR) /* dynamic-range compressor */
DSP_PROC_DB_STOP DSP_PROC_DB_STOP

View file

@ -28,6 +28,9 @@
#include "dsp_misc.h" #include "dsp_misc.h"
#include "eq.h" #include "eq.h"
#include "pga.h" #include "pga.h"
#include "surround.h"
#include "afr.h"
#include "pbe.h"
#ifdef HAVE_PITCHCONTROL #ifdef HAVE_PITCHCONTROL
#include "tdspeed.h" #include "tdspeed.h"
#endif #endif

251
lib/rbcodec/dsp/pbe.c Normal file
View file

@ -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);

29
lib/rbcodec/dsp/pbe.h Normal file
View file

@ -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 <stdbool.h>
void dsp_pbe_enable(int var);
void dsp_pbe_precut(int var);
#endif /* PBE_H */

333
lib/rbcodec/dsp/surround.c Normal file
View file

@ -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);

View file

@ -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 <stdbool.h>
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 */

View file

@ -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}{ \opt{swcodec}{
\section{Compressor} \section{Compressor}
The \setting{Compressor} reduces, or compresses, the dynamic range of the audio The \setting{Compressor} reduces, or compresses, the dynamic range of the audio