diff options
author | Bertrik Sikken <bertrik@sikken.nl> | 2012-05-01 03:58:27 -0400 |
---|---|---|
committer | Thomas Martitz <kugel@rockbox.org> | 2012-05-28 11:34:15 +0200 |
commit | afc96087f8a6282cf732d142a4db7a3d604d39d8 (patch) | |
tree | ccdf78007bb087ab658edaa951f245ad4141bae2 /lib/rbcodec/dsp/crossfeed.c | |
parent | 08f5224b1bf1293ab1d59fdfbf9045561733c38d (diff) | |
download | rockbox-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.c | 133 |
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 */ |
34 | void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p); | 35 | void crossfeed_process(struct dsp_proc_entry *this, |
36 | struct dsp_buffer **buf_p); | ||
37 | void 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 | ||
75 | static int crossfeed_type = CROSSFEED_TYPE_NONE; | ||
76 | |||
56 | /* Discard the sample histories */ | 77 | /* Discard the sample histories */ |
57 | static void crossfeed_flush(struct dsp_proc_entry *this) | 78 | static 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 */ |
94 | void dsp_crossfeed_enable(bool enable) | 139 | void 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 | |||
238 | void 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 */ |
186 | static intptr_t crossfeed_configure(struct dsp_proc_entry *this, | 277 | static 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; |