2014-08-22 13:30:35 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* 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 */
|
|
|
|
|
2015-01-22 02:24:15 +00:00
|
|
|
unsigned int total_len = (B0_SIZE + B2_SIZE + B3_SIZE) * 2;
|
2015-01-28 06:30:54 +00:00
|
|
|
if (handle > 0)
|
|
|
|
memset(core_get_data(handle),0,sizeof(int32_t) * total_len);
|
2015-01-22 02:24:15 +00:00
|
|
|
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;
|
2014-08-22 13:30:35 +00:00
|
|
|
|
|
|
|
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);
|