summaryrefslogtreecommitdiff
path: root/lib/rbcodec/dsp/crossfeed.c
diff options
context:
space:
mode:
authorBertrik Sikken <bertrik@sikken.nl>2012-05-01 03:58:27 -0400
committerThomas Martitz <kugel@rockbox.org>2012-05-28 11:34:15 +0200
commitafc96087f8a6282cf732d142a4db7a3d604d39d8 (patch)
treeccdf78007bb087ab658edaa951f245ad4141bae2 /lib/rbcodec/dsp/crossfeed.c
parent08f5224b1bf1293ab1d59fdfbf9045561733c38d (diff)
downloadrockbox-afc96087f8a6282cf732d142a4db7a3d604d39d8.tar.gz
rockbox-afc96087f8a6282cf732d142a4db7a3d604d39d8.zip
New crossfeed algorithm for Rockbox: "Meier" crossfeed
Emulates the basic "Meier" crossfeed (2 capacitors, 3 resistors) as discussed in http://www.meier-audio.homepage.t-online.de/passivefilter.htm This crossfeed blends a bit of low-pass filtered L signal into the R signal (and vice versa) while adding about 300 us delay to the crossfed-signal. A difference with the crossfeed already present in rockbox, is that this algorithm keeps the total spectrum flat (the one currently in rockbox accentuates low-frequency signals, making it sound a bit muffled). This implementation is quite lightweight, just 3 multiplies per left-right pair of samples. Has a default C implementation and optimized assembly versions for ARM and Coldfire. The crossfeed effect is quite subtle and is noticeable mostly one albums that have very strong left-right separation (e.g. one instrument only on the left, another only on the right). In the user interface, the new crossfeed option appears as "Meier" and is not configureable. The existing crossfeed is renamed to "Custom" as it allows itself to be customised. There is no entry for the user manual yet. Change-Id: Iaa100616fe0fcd7e16f08cdb9a7f41501973eee1
Diffstat (limited to 'lib/rbcodec/dsp/crossfeed.c')
-rw-r--r--lib/rbcodec/dsp/crossfeed.c133
1 files changed, 116 insertions, 17 deletions
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 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2006 Thom Johansen 10 * Copyright (C) 2006 Thom Johansen
11 * Copyright (C) 2010 Bertrik Sikken
11 * Copyright (C) 2012 Michael Sevakis 12 * Copyright (C) 2012 Michael Sevakis
12 * 13 *
13 * This program is free software; you can redistribute it and/or 14 * This program is free software; you can redistribute it and/or
@@ -31,7 +32,11 @@
31#include <string.h> 32#include <string.h>
32 33
33/* Implemented here or in target assembly code */ 34/* Implemented here or in target assembly code */
34void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p); 35void crossfeed_process(struct dsp_proc_entry *this,
36 struct dsp_buffer **buf_p);
37void crossfeed_meier_process(struct dsp_proc_entry *this,
38 struct dsp_buffer **buf_p);
39
35 40
36/** 41/**
37 * Applies crossfeed to the stereo signal. 42 * Applies crossfeed to the stereo signal.
@@ -46,20 +51,44 @@ static struct crossfeed_state
46{ 51{
47 int32_t gain; /* 00h: Direct path gain */ 52 int32_t gain; /* 00h: Direct path gain */
48 int32_t coefs[3]; /* 04h: Coefficients for the shelving filter */ 53 int32_t coefs[3]; /* 04h: Coefficients for the shelving filter */
49 int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */ 54 union
50 int32_t delay[13*2]; /* 20h: Delay line buffer (L + R interleaved) */ 55 {
56 struct /* 10h: Data for meier crossfeed */
57 {
58 int32_t vcl;
59 int32_t vcr;
60 int32_t vdiff;
61 int32_t coef1;
62 int32_t coef2;
63 };
64 struct /* 10h: Data for custom crossfeed */
65 {
66 int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */
67 int32_t delay[13*2];/* 20h: Delay line buffer (L + R interleaved) */
68 };
69 };
51 int32_t *index; /* 88h: Current pointer into the delay line */ 70 int32_t *index; /* 88h: Current pointer into the delay line */
52 struct dsp_config *dsp; /* 8ch: Current DSP */ 71 struct dsp_config *dsp; /* 8ch: Current DSP */
53 /* 90h */ 72 /* 90h */
54} crossfeed_state IBSS_ATTR; 73} crossfeed_state IBSS_ATTR;
55 74
75static int crossfeed_type = CROSSFEED_TYPE_NONE;
76
56/* Discard the sample histories */ 77/* Discard the sample histories */
57static void crossfeed_flush(struct dsp_proc_entry *this) 78static void crossfeed_flush(struct dsp_proc_entry *this)
58{ 79{
59 struct crossfeed_state *state = (void *)this->data; 80 struct crossfeed_state *state = (void *)this->data;
60 memset(state->history, 0, sizeof (state->history)); 81
61 memset(state->delay, 0, sizeof (state->delay)); 82 if (crossfeed_type == CROSSFEED_TYPE_CUSTOM)
62 state->index = state->delay; 83 {
84 memset(state->history, 0,
85 sizeof (state->history) + sizeof (state->delay));
86 state->index = state->delay;
87 }
88 else
89 {
90 state->vcl = state->vcr = state->vdiff = 0;
91 }
63} 92}
64 93
65 94
@@ -74,30 +103,48 @@ static void crossfeed_process_new_format(struct dsp_proc_entry *this,
74 103
75 DSP_PRINT_FORMAT(DSP_PROC_CROSSFEED, DSP_PROC_CROSSFEED, buf->format); 104 DSP_PRINT_FORMAT(DSP_PROC_CROSSFEED, DSP_PROC_CROSSFEED, buf->format);
76 105
106 bool was_active = dsp_proc_active(state->dsp, DSP_PROC_CROSSFEED);
77 bool active = buf->format.num_channels >= 2; 107 bool active = buf->format.num_channels >= 2;
78 dsp_proc_activate(state->dsp, DSP_PROC_CROSSFEED, active); 108 dsp_proc_activate(state->dsp, DSP_PROC_CROSSFEED, active);
79 109
80 if (!active) 110 if (!active)
81 { 111 {
82 /* Can't do this. Sleep until next change */ 112 /* Can't do this. Sleep until next change */
83 crossfeed_flush(this);
84 DEBUGF(" DSP_PROC_CROSSFEED- deactivated\n"); 113 DEBUGF(" DSP_PROC_CROSSFEED- deactivated\n");
85 return; 114 return;
86 } 115 }
87 116
88 /* Switch to the real function and call it once */ 117 dsp_proc_fn_type fn = crossfeed_process;
89 this->process[0] = crossfeed_process; 118
119 if (crossfeed_type != CROSSFEED_TYPE_CUSTOM)
120 {
121 /* 1 / (F.Rforward.C) */
122 state->coef1 = (0x7fffffff / NATIVE_FREQUENCY) * 2128;
123 /* 1 / (F.Rcross.C) */
124 state->coef2 = (0x7fffffff / NATIVE_FREQUENCY) * 1000;
125 fn = crossfeed_meier_process;
126 }
127
128 if (!was_active || this->process[0] != fn)
129 {
130 crossfeed_flush(this); /* Going online or actual type change */
131 this->process[0] = fn; /* Set real function */
132 }
133
134 /* Call it once */
90 dsp_proc_call(this, buf_p, (unsigned)buf->format.changed - 1); 135 dsp_proc_call(this, buf_p, (unsigned)buf->format.changed - 1);
91} 136}
92 137
93/* Enable or disable the crossfeed */ 138/* Set the type of crossfeed to use */
94void dsp_crossfeed_enable(bool enable) 139void dsp_set_crossfeed_type(int type)
95{ 140{
96 if (enable != !crossfeed_state.dsp) 141 if (type == crossfeed_type)
97 return; 142 return; /* No change */
143
144 crossfeed_type = type;
98 145
99 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); 146 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
100 dsp_proc_enable(dsp, DSP_PROC_CROSSFEED, enable); 147 dsp_proc_enable(dsp, DSP_PROC_CROSSFEED, type != CROSSFEED_TYPE_NONE);
101} 148}
102 149
103/* Set the gain of the dry mix */ 150/* Set the gain of the dry mix */
@@ -182,6 +229,50 @@ void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
182} 229}
183#endif /* CPU */ 230#endif /* CPU */
184 231
232#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
233/**
234 * Implementation of the "simple" passive crossfeed circuit by Jan Meier.
235 * See also: http://www.meier-audio.homepage.t-online.de/passivefilter.htm
236 */
237
238void crossfeed_meier_process(struct dsp_proc_entry *this,
239 struct dsp_buffer **buf_p)
240{
241 struct dsp_buffer *buf = *buf_p;
242
243 /* Get filter state */
244 struct crossfeed_state *state = (struct crossfeed_state *)this->data;
245 int32_t vcl = state->vcl;
246 int32_t vcr = state->vcr;
247 int32_t vdiff = state->vdiff;
248 int32_t coef1 = state->coef1;
249 int32_t coef2 = state->coef2;
250
251 int count = buf->remcount;
252
253 for (int i = 0; i < count; i++)
254 {
255 /* Calculate new output */
256 int32_t lout = buf->p32[0][i] + vcl;
257 int32_t rout = buf->p32[1][i] + vcr;
258 buf->p32[0][i] = lout;
259 buf->p32[1][i] = rout;
260
261 /* Update filter state */
262 int32_t common = FRACMUL(vdiff, coef2);
263 vcl -= FRACMUL(vcl, coef1) + common;
264 vcr -= FRACMUL(vcr, coef1) - common;
265
266 vdiff = lout - rout;
267 }
268
269 /* Store filter state */
270 state->vcl = vcl;
271 state->vcr = vcr;
272 state->vdiff = vdiff;
273}
274#endif /* CPU */
275
185/* DSP message hook */ 276/* DSP message hook */
186static intptr_t crossfeed_configure(struct dsp_proc_entry *this, 277static intptr_t crossfeed_configure(struct dsp_proc_entry *this,
187 struct dsp_config *dsp, 278 struct dsp_config *dsp,
@@ -191,11 +282,19 @@ static intptr_t crossfeed_configure(struct dsp_proc_entry *this,
191 switch (setting) 282 switch (setting)
192 { 283 {
193 case DSP_PROC_INIT: 284 case DSP_PROC_INIT:
194 this->data = (intptr_t)&crossfeed_state; 285 if (value == 0)
286 {
287 /* New object */
288 this->data = (intptr_t)&crossfeed_state;
289 this->process[1] = crossfeed_process_new_format;
290 ((struct crossfeed_state *)this->data)->dsp = dsp;
291 }
292
293 /* Force format change call each time */
195 this->process[0] = crossfeed_process_new_format; 294 this->process[0] = crossfeed_process_new_format;
196 this->process[1] = crossfeed_process_new_format;
197 ((struct crossfeed_state *)this->data)->dsp = dsp;
198 dsp_proc_activate(dsp, DSP_PROC_CROSSFEED, true); 295 dsp_proc_activate(dsp, DSP_PROC_CROSSFEED, true);
296 break;
297
199 case DSP_FLUSH: 298 case DSP_FLUSH:
200 crossfeed_flush(this); 299 crossfeed_flush(this);
201 break; 300 break;