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 (Meier)" + + + *: none + swcodec: "Simple" + + + + id: LANG_CROSSFEED_CUSTOM + desc: in sound settings + user: core + + *: none + swcodec: "Custom" + + + *: 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,