diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index fc575a3764..7366a80030 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -13052,3 +13052,37 @@
*: "Western European"
+
+ id: LANG_CROSSFEED_MEIER
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "Simple (Meier)"
+
+
+ *: none
+ swcodec: "Simple"
+
+
+
+ id: LANG_CROSSFEED_CUSTOM
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ swcodec: "Custom"
+
+
+ *: none
+ swcodec: "Custom"
+
+
diff --git a/apps/plugin.c b/apps/plugin.c
index afb336ebdc..e0e565e504 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -565,7 +565,7 @@ static const struct plugin_api rockbox_api = {
audio_set_output_source,
audio_set_input_source,
#endif
- dsp_crossfeed_enable,
+ dsp_set_crossfeed_type ,
dsp_eq_enable,
dsp_dither_enable,
#ifdef HAVE_PITCHCONTROL
diff --git a/apps/plugin.h b/apps/plugin.h
index b1d3c16979..bb2778164b 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -682,7 +682,7 @@ struct plugin_api {
void (*audio_set_output_source)(int monitor);
void (*audio_set_input_source)(int source, unsigned flags);
#endif
- void (*dsp_crossfeed_enable)(bool enable);
+ void (*dsp_set_crossfeed_type)(int type);
void (*dsp_eq_enable)(bool enable);
void (*dsp_dither_enable)(bool enable);
#ifdef HAVE_PITCHCONTROL
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
index 7f92fb7c69..bcef4c66bf 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.c
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -457,8 +457,9 @@ static void sync_audio_setting(int setting, bool global)
break;
case MPEG_AUDIO_CROSSFEED:
- rb->dsp_crossfeed_enable((global || settings.crossfeed) ?
- rb->global_settings->crossfeed : false);
+ rb->dsp_set_crossfeed_type((global || settings.crossfeed) ?
+ rb->global_settings->crossfeed :
+ CROSSFEED_TYPE_NONE);
break;
case MPEG_AUDIO_EQUALIZER:
diff --git a/apps/settings.c b/apps/settings.c
index d777eb1565..3bf9c5bf17 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -979,7 +979,7 @@ void settings_apply(bool read_disk)
audio_set_crossfade(global_settings.crossfade);
#endif
replaygain_update();
- dsp_crossfeed_enable(global_settings.crossfeed);
+ dsp_set_crossfeed_type(global_settings.crossfeed);
dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain);
dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
global_settings.crossfeed_hf_attenuation,
diff --git a/apps/settings.h b/apps/settings.h
index 55d3344cb2..ef0bae520e 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -325,7 +325,7 @@ struct user_settings
struct replaygain_settings replaygain_settings;
/* Crossfeed */
- bool crossfeed; /* enable crossfeed */
+ int crossfeed; /* crossfeed type */
unsigned int crossfeed_direct_gain; /* dB x 10 */
unsigned int crossfeed_cross_gain; /* dB x 10 */
unsigned int crossfeed_hf_attenuation; /* dB x 10 */
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 10d00d5c89..accd51dfe1 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1402,8 +1402,10 @@ const struct settings_list settings[] = {
#endif
/* crossfeed */
- OFFON_SETTING(F_SOUNDSETTING, crossfeed, LANG_CROSSFEED, false,
- "crossfeed", dsp_crossfeed_enable),
+ CHOICE_SETTING(F_SOUNDSETTING, crossfeed, LANG_CROSSFEED, 0,"crossfeed",
+ "off,meier,custom", dsp_set_crossfeed_type, 3,
+ ID2P(LANG_OFF), ID2P(LANG_CROSSFEED_MEIER),
+ ID2P(LANG_CROSSFEED_CUSTOM)),
INT_SETTING_NOWRAP(F_SOUNDSETTING, crossfeed_direct_gain,
LANG_CROSSFEED_DIRECT_GAIN, -15,
"crossfeed direct gain", UNIT_DB, -60, 0, 5,
diff --git a/lib/rbcodec/dsp/crossfeed.c b/lib/rbcodec/dsp/crossfeed.c
index 344addadd7..3fb51a7594 100644
--- a/lib/rbcodec/dsp/crossfeed.c
+++ b/lib/rbcodec/dsp/crossfeed.c
@@ -8,6 +8,7 @@
* $Id$
*
* Copyright (C) 2006 Thom Johansen
+ * Copyright (C) 2010 Bertrik Sikken
* Copyright (C) 2012 Michael Sevakis
*
* This program is free software; you can redistribute it and/or
@@ -31,7 +32,11 @@
#include
/* Implemented here or in target assembly code */
-void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p);
+void crossfeed_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p);
+void crossfeed_meier_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p);
+
/**
* Applies crossfeed to the stereo signal.
@@ -46,20 +51,44 @@ static struct crossfeed_state
{
int32_t gain; /* 00h: Direct path gain */
int32_t coefs[3]; /* 04h: Coefficients for the shelving filter */
- int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */
- int32_t delay[13*2]; /* 20h: Delay line buffer (L + R interleaved) */
+ union
+ {
+ struct /* 10h: Data for meier crossfeed */
+ {
+ int32_t vcl;
+ int32_t vcr;
+ int32_t vdiff;
+ int32_t coef1;
+ int32_t coef2;
+ };
+ struct /* 10h: Data for custom crossfeed */
+ {
+ int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */
+ int32_t delay[13*2];/* 20h: Delay line buffer (L + R interleaved) */
+ };
+ };
int32_t *index; /* 88h: Current pointer into the delay line */
struct dsp_config *dsp; /* 8ch: Current DSP */
/* 90h */
} crossfeed_state IBSS_ATTR;
+static int crossfeed_type = CROSSFEED_TYPE_NONE;
+
/* Discard the sample histories */
static void crossfeed_flush(struct dsp_proc_entry *this)
{
struct crossfeed_state *state = (void *)this->data;
- memset(state->history, 0, sizeof (state->history));
- memset(state->delay, 0, sizeof (state->delay));
- state->index = state->delay;
+
+ if (crossfeed_type == CROSSFEED_TYPE_CUSTOM)
+ {
+ memset(state->history, 0,
+ sizeof (state->history) + sizeof (state->delay));
+ state->index = state->delay;
+ }
+ else
+ {
+ state->vcl = state->vcr = state->vdiff = 0;
+ }
}
@@ -74,30 +103,48 @@ static void crossfeed_process_new_format(struct dsp_proc_entry *this,
DSP_PRINT_FORMAT(DSP_PROC_CROSSFEED, DSP_PROC_CROSSFEED, buf->format);
+ bool was_active = dsp_proc_active(state->dsp, DSP_PROC_CROSSFEED);
bool active = buf->format.num_channels >= 2;
dsp_proc_activate(state->dsp, DSP_PROC_CROSSFEED, active);
if (!active)
{
/* Can't do this. Sleep until next change */
- crossfeed_flush(this);
DEBUGF(" DSP_PROC_CROSSFEED- deactivated\n");
return;
}
- /* Switch to the real function and call it once */
- this->process[0] = crossfeed_process;
+ dsp_proc_fn_type fn = crossfeed_process;
+
+ if (crossfeed_type != CROSSFEED_TYPE_CUSTOM)
+ {
+ /* 1 / (F.Rforward.C) */
+ state->coef1 = (0x7fffffff / NATIVE_FREQUENCY) * 2128;
+ /* 1 / (F.Rcross.C) */
+ state->coef2 = (0x7fffffff / NATIVE_FREQUENCY) * 1000;
+ fn = crossfeed_meier_process;
+ }
+
+ if (!was_active || this->process[0] != fn)
+ {
+ crossfeed_flush(this); /* Going online or actual type change */
+ this->process[0] = fn; /* Set real function */
+ }
+
+ /* Call it once */
dsp_proc_call(this, buf_p, (unsigned)buf->format.changed - 1);
}
-/* Enable or disable the crossfeed */
-void dsp_crossfeed_enable(bool enable)
+/* Set the type of crossfeed to use */
+void dsp_set_crossfeed_type(int type)
{
- if (enable != !crossfeed_state.dsp)
- return;
+ if (type == crossfeed_type)
+ return; /* No change */
+
+ crossfeed_type = type;
struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
- dsp_proc_enable(dsp, DSP_PROC_CROSSFEED, enable);
+ dsp_proc_enable(dsp, DSP_PROC_CROSSFEED, type != CROSSFEED_TYPE_NONE);
}
/* Set the gain of the dry mix */
@@ -182,6 +229,50 @@ void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
}
#endif /* CPU */
+#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
+/**
+ * Implementation of the "simple" passive crossfeed circuit by Jan Meier.
+ * See also: http://www.meier-audio.homepage.t-online.de/passivefilter.htm
+ */
+
+void crossfeed_meier_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *buf = *buf_p;
+
+ /* Get filter state */
+ struct crossfeed_state *state = (struct crossfeed_state *)this->data;
+ int32_t vcl = state->vcl;
+ int32_t vcr = state->vcr;
+ int32_t vdiff = state->vdiff;
+ int32_t coef1 = state->coef1;
+ int32_t coef2 = state->coef2;
+
+ int count = buf->remcount;
+
+ for (int i = 0; i < count; i++)
+ {
+ /* Calculate new output */
+ int32_t lout = buf->p32[0][i] + vcl;
+ int32_t rout = buf->p32[1][i] + vcr;
+ buf->p32[0][i] = lout;
+ buf->p32[1][i] = rout;
+
+ /* Update filter state */
+ int32_t common = FRACMUL(vdiff, coef2);
+ vcl -= FRACMUL(vcl, coef1) + common;
+ vcr -= FRACMUL(vcr, coef1) - common;
+
+ vdiff = lout - rout;
+ }
+
+ /* Store filter state */
+ state->vcl = vcl;
+ state->vcr = vcr;
+ state->vdiff = vdiff;
+}
+#endif /* CPU */
+
/* DSP message hook */
static intptr_t crossfeed_configure(struct dsp_proc_entry *this,
struct dsp_config *dsp,
@@ -191,11 +282,19 @@ static intptr_t crossfeed_configure(struct dsp_proc_entry *this,
switch (setting)
{
case DSP_PROC_INIT:
- this->data = (intptr_t)&crossfeed_state;
+ if (value == 0)
+ {
+ /* New object */
+ this->data = (intptr_t)&crossfeed_state;
+ this->process[1] = crossfeed_process_new_format;
+ ((struct crossfeed_state *)this->data)->dsp = dsp;
+ }
+
+ /* Force format change call each time */
this->process[0] = crossfeed_process_new_format;
- this->process[1] = crossfeed_process_new_format;
- ((struct crossfeed_state *)this->data)->dsp = dsp;
dsp_proc_activate(dsp, DSP_PROC_CROSSFEED, true);
+ break;
+
case DSP_FLUSH:
crossfeed_flush(this);
break;
diff --git a/lib/rbcodec/dsp/crossfeed.h b/lib/rbcodec/dsp/crossfeed.h
index 63261bde9f..2c4d47dba5 100644
--- a/lib/rbcodec/dsp/crossfeed.h
+++ b/lib/rbcodec/dsp/crossfeed.h
@@ -21,7 +21,14 @@
#ifndef CROSSFEED_H
#define CROSSFEED_H
-void dsp_crossfeed_enable(bool enable);
+enum crossfeed_type
+{
+ CROSSFEED_TYPE_NONE,
+ CROSSFEED_TYPE_MEIER,
+ CROSSFEED_TYPE_CUSTOM,
+};
+
+void dsp_set_crossfeed_type(int type);
void dsp_set_crossfeed_direct_gain(int gain);
void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff);
diff --git a/lib/rbcodec/dsp/dsp_arm.S b/lib/rbcodec/dsp/dsp_arm.S
index 1674d6617a..4fdaf8d5a8 100644
--- a/lib/rbcodec/dsp/dsp_arm.S
+++ b/lib/rbcodec/dsp/dsp_arm.S
@@ -8,6 +8,8 @@
* $Id$
*
* Copyright (C) 2006-2007 Thom Johansen
+ * Copyright (C) 2010 Bertrik Sikken
+ * Copyright (C) 2012 Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -246,6 +248,48 @@ crossfeed_process:
ldmpc regs=r4-r11
.size crossfeed_process, .-crossfeed_process
+/****************************************************************************
+ * void crossfeed_meier_process(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
+ */
+ .section .text
+ .global crossfeed_meier_process
+crossfeed_meier_process:
+ @ input: r0 = this, r1 = buf_p
+ ldr r1, [r1] @ r1 = buf = *buf_p;
+ ldr r0, [r0] @ r0 = this->data = &crossfeed_state
+ stmfd sp!, { r4-r10, lr } @ stack non-volatile context
+ ldmia r1, { r1-r3 } @ r1 = buf->remcout, r2=p32[0], r3=p32[1]
+ add r0, r0, #16 @ r0 = &state->vcl
+ ldmia r0, { r4-r8 } @ r4 = vcl, r5 = vcr, r6 = vdiff
+ @ r7 = coef1, r8 = coef2
+.cfm_loop:
+ ldr r12, [r2] @ r12 = lout
+ ldr r14, [r3] @ r14 = rout
+ smull r9, r10, r8, r6 @ r9, r10 = common = coef2*vdiff
+ add r12, r12, r4 @ lout += vcl
+ add r14, r14, r5 @ rout += vcr
+ sub r6, r12, r14 @ r6 = vdiff = lout - rout
+ str r12, [r2], #4 @ store left channel
+ str r14, [r3], #4 @ store right channel
+ rsbs r12, r9, #0 @ r12 = -common (lo)
+ rsc r14, r10, #0 @ r14 = -common (hi)
+ smlal r9, r10, r7, r4 @ r9, r10 = res1 = coef1*vcl + common
+ smlal r12, r14, r7, r5 @ r12, r14 = res2 = coef1*vcr - common
+ subs r1, r1, #1 @ count--
+ mov r9, r9, lsr #31 @ r9 = convert res1 to s0.31
+ orr r9, r9, r10, asl #1 @ .
+ mov r12, r12, lsr #31 @ r12 = convert res2 to s0.31
+ orr r12, r12, r14, asl #1 @ .
+ sub r4, r4, r9 @ r4 = vcl -= res1
+ sub r5, r5, r12 @ r5 = vcr -= res2
+ bgt .cfm_loop @ more samples?
+
+ stmia r0, { r4-r6 } @ save vcl, vcr, vdiff
+ ldmpc regs=r4-r10 @ restore non-volatile context, return
+ .size crossfeed_meier_process, .-crossfeed_meier_process
+
+
/****************************************************************************
* int lin_resample_resample(struct resample_data *data,
* struct dsp_buffer *src,
diff --git a/lib/rbcodec/dsp/dsp_cf.S b/lib/rbcodec/dsp/dsp_cf.S
index c710df5177..7d193e0957 100644
--- a/lib/rbcodec/dsp/dsp_cf.S
+++ b/lib/rbcodec/dsp/dsp_cf.S
@@ -8,7 +8,8 @@
* $Id$
*
* Copyright (C) 2006 Thom Johansen
- * Portions Copyright (C) 2007 Michael Sevakis
+ * Copyright (C) 2007, 2012 Michael Sevakis
+ * Copyright (C) 2010 Bertrik Sikken
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -133,6 +134,50 @@ crossfeed_process:
rts |
.size crossfeed_process,.-crossfeed_process
+/****************************************************************************
+ * void crossfeed_meier_process(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
+ */
+ .section .text
+ .global crossfeed_meier_process
+crossfeed_meier_process:
+ | input: 4(sp) = this, 8(sp) = buf_p
+ movem.l 4(%sp), %a0-%a1 | %a0 = this, %a1 = buf_p
+ lea.l -24(%sp), %sp | save non-volatiles
+ movem.l %d2-%d6/%a2, (%sp) | .
+ move.l (%a0), %a0 | %a0 = &this->data = &crossfeed_state
+ move.l (%a1), %a1 | %a1 = buf = *buf_p
+ movem.l 16(%a0), %d1-%d5 | %d1 = vcl, %d2 = vcr, %d3 = vdiff,
+ | %d4 = coef1, %d5 = coef2
+ movem.l (%a1), %d0/%a1-%a2 | %d0 = count = buf->remcount
+ | %a1 = p32[0], %a2 = p32[1]
+ | Register usage in loop:
+ | %d0 = count, %d1 = vcl, %d2 = vcr, %d3 = vdiff/lout,
+ | %d4 = coef1, %d5 = coef2, %d6 = rout/scratch
+ | %a1 = p32[0], %a2 = p32[1]
+10: | loop
+ mac.l %d5, %d3, %acc0 | %acc0 = common = coef2*vdiff
+ move.l %acc0, %acc1 | copy common
+ mac.l %d4, %d1, (%a1), %d3, %acc0 | %acc0 += coef1*vcl, %d3 = lout
+ msac.l %d4, %d2, (%a2), %d6, %acc1 | %acc1 -= coef1*vcr, %d6 = rout
+ add.l %d1, %d3 | lout += vcl
+ add.l %d2, %d6 | rout += vcr
+ move.l %d3, (%a1)+ | store left channel, pos inc
+ move.l %d6, (%a2)+ | store right channel, pos inc
+ sub.l %d6, %d3 | vdiff = lout - rout
+ movclr.l %acc0, %d6 | %d4 = fetch res1 in s0.31
+ sub.l %d6, %d1 | vcl -= res1
+ movclr.l %acc1, %d6 | %d5 = fetch -res2 in s0.31
+ add.l %d6, %d2 | vcr += -res2
+ subq.l #1, %d0 | count--
+ bgt 10b | loop | more samples?
+ |
+ movem.l %d1-%d3, 16(%a0) | save vcl, vcr, vdiff
+ movem.l (%sp), %d2-%d6/%a2 | restore non-volatiles
+ lea.l 24(%sp), %sp | .
+ rts |
+ .size crossfeed_meier_process, .-crossfeed_meier_process
+
/****************************************************************************
* int lin_resample_resample(struct resample_data *data,
* struct dsp_buffer *src,