summaryrefslogtreecommitdiff
path: root/lib/rbcodec/dsp/channel_mode.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/dsp/channel_mode.c')
-rw-r--r--lib/rbcodec/dsp/channel_mode.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/lib/rbcodec/dsp/channel_mode.c b/lib/rbcodec/dsp/channel_mode.c
new file mode 100644
index 0000000000..5b678887c2
--- /dev/null
+++ b/lib/rbcodec/dsp/channel_mode.c
@@ -0,0 +1,264 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Thom Johansen
11 * Copyright (C) 2012 Michael Sevakis
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include "config.h"
23#include "system.h"
24#include "dsp.h"
25#include "settings.h"
26#include "sound.h"
27#include "fixedpoint.h"
28#include "fracmul.h"
29#include "dsp_proc_entry.h"
30
31#if 0
32/* SOUND_CHAN_STEREO mode is a noop so has no function - just outline one for
33 * completeness. */
34void channel_mode_proc_stereo(struct dsp_proc_entry *this,
35 struct dsp_buffer **buf_p);
36#endif
37void channel_mode_proc_mono(struct dsp_proc_entry *this,
38 struct dsp_buffer **buf_p);
39void channel_mode_proc_mono_left(struct dsp_proc_entry *this,
40 struct dsp_buffer **buf_p);
41void channel_mode_proc_mono_right(struct dsp_proc_entry *this,
42 struct dsp_buffer **buf_p);
43void channel_mode_proc_custom(struct dsp_proc_entry *this,
44 struct dsp_buffer **buf_p);
45void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
46 struct dsp_buffer **buf_p);
47
48static struct channel_mode_data
49{
50 long sw_gain; /* 00h: for mode: custom */
51 long sw_cross; /* 04h: for mode: custom */
52 struct dsp_config *dsp;
53 int mode;
54 const dsp_proc_fn_type fns[SOUND_CHAN_NUM_MODES];
55} channel_mode_data =
56{
57 .sw_gain = 0,
58 .sw_cross = 0,
59 .mode = SOUND_CHAN_STEREO,
60 .fns =
61 {
62 [SOUND_CHAN_STEREO] = NULL,
63 [SOUND_CHAN_MONO] = channel_mode_proc_mono,
64 [SOUND_CHAN_CUSTOM] = channel_mode_proc_custom,
65 [SOUND_CHAN_MONO_LEFT] = channel_mode_proc_mono_left,
66 [SOUND_CHAN_MONO_RIGHT] = channel_mode_proc_mono_right,
67 [SOUND_CHAN_KARAOKE] = channel_mode_proc_karaoke,
68 },
69};
70
71static dsp_proc_fn_type get_process_fn(void)
72{
73 return channel_mode_data.fns[channel_mode_data.mode];
74}
75
76#if 0
77/* SOUND_CHAN_STEREO mode is a noop so has no function - just outline one for
78 * completeness. */
79void channel_mode_proc_stereo(struct dsp_proc_entry *this,
80 struct dsp_buffer **buf_p)
81{
82 /* The channels are each just themselves */
83 (void)this; (void)buf_p;
84}
85#endif
86
87#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
88/* Unoptimized routines */
89void channel_mode_proc_mono(struct dsp_proc_entry *this,
90 struct dsp_buffer **buf_p)
91{
92 struct dsp_buffer *buf = *buf_p;
93 int32_t *sl = buf->p32[0];
94 int32_t *sr = buf->p32[1];
95 int count = buf->remcount;
96
97 do
98 {
99 int32_t lr = *sl / 2 + *sr / 2;
100 *sl++ = lr;
101 *sr++ = lr;
102 }
103 while (--count > 0);
104
105 (void)this;
106}
107
108void channel_mode_proc_custom(struct dsp_proc_entry *this,
109 struct dsp_buffer **buf_p)
110{
111 struct channel_mode_data *data = (void *)this->data;
112 struct dsp_buffer *buf = *buf_p;
113
114 int32_t *sl = buf->p32[0];
115 int32_t *sr = buf->p32[1];
116 int count = buf->remcount;
117
118 const int32_t gain = data->sw_gain;
119 const int32_t cross = data->sw_cross;
120
121 do
122 {
123 int32_t l = *sl;
124 int32_t r = *sr;
125 *sl++ = FRACMUL(l, gain) + FRACMUL(r, cross);
126 *sr++ = FRACMUL(r, gain) + FRACMUL(l, cross);
127 }
128 while (--count > 0);
129}
130
131void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
132 struct dsp_buffer **buf_p)
133{
134 struct dsp_buffer *buf = *buf_p;
135 int32_t *sl = buf->p32[0];
136 int32_t *sr = buf->p32[1];
137 int count = buf->remcount;
138
139 do
140 {
141 int32_t ch = *sl / 2 - *sr / 2;
142 *sl++ = ch;
143 *sr++ = -ch;
144 }
145 while (--count > 0);
146
147 (void)this;
148}
149#endif /* CPU */
150
151void channel_mode_proc_mono_left(struct dsp_proc_entry *this,
152 struct dsp_buffer **buf_p)
153{
154 /* Just copy over the other channel */
155 struct dsp_buffer *buf = *buf_p;
156 memcpy(buf->p32[1], buf->p32[0], buf->remcount * sizeof (int32_t));
157 (void)this;
158}
159
160void channel_mode_proc_mono_right(struct dsp_proc_entry *this,
161 struct dsp_buffer **buf_p)
162{
163 /* Just copy over the other channel */
164 struct dsp_buffer *buf = *buf_p;
165 memcpy(buf->p32[0], buf->p32[1], buf->remcount * sizeof (int32_t));
166 (void)this;
167}
168
169/* This is the initial function pointer when first enabled/changed in order
170 * to facilitate verification of the format compatibility at the proper time
171 * This gets called for changes even if stage is inactive. */
172static void channel_mode_process_new_format(struct dsp_proc_entry *this,
173 struct dsp_buffer **buf_p)
174{
175 struct channel_mode_data *data = (void *)this->data;
176 struct dsp_buffer *buf = *buf_p;
177
178 DSP_PRINT_FORMAT(DSP_PROC_CHANNEL_MODE, DSP_PROC_CHANNEL_MODE,
179 buf->format);
180
181 bool active = buf->format.num_channels >= 2;
182 dsp_proc_activate(data->dsp, DSP_PROC_CHANNEL_MODE, active);
183
184 if (!active)
185 {
186 /* Can't do this. Sleep until next change. */
187 DEBUGF(" DSP_PROC_CHANNEL_MODE- deactivated\n");
188 return;
189 }
190
191 /* Switch to the real function and call it once */
192 this->process[0] = get_process_fn();
193 dsp_proc_call(this, buf_p, (unsigned)buf->format.changed - 1);
194}
195
196void channel_mode_set_config(int value)
197{
198 if (value < 0 || value >= SOUND_CHAN_NUM_MODES)
199 value = SOUND_CHAN_STEREO; /* Out of range */
200
201 if (value == channel_mode_data.mode)
202 return;
203
204 channel_mode_data.mode = value;
205 dsp_proc_enable(dsp_get_config(CODEC_IDX_AUDIO), DSP_PROC_CHANNEL_MODE,
206 value != SOUND_CHAN_STEREO);
207}
208
209void channel_mode_custom_set_width(int value)
210{
211 long width, straight, cross;
212
213 width = value * 0x7fffff / 100;
214
215 if (value <= 100)
216 {
217 straight = (0x7fffff + width) / 2;
218 cross = straight - width;
219 }
220 else
221 {
222 /* straight = (1 + width) / (2 * width) */
223 straight = fp_div(0x7fffff + width, width, 22);
224 cross = straight - 0x7fffff;
225 }
226
227 channel_mode_data.sw_gain = straight << 8;
228 channel_mode_data.sw_cross = cross << 8;
229}
230
231/* DSP message hook */
232static intptr_t channel_mode_configure(struct dsp_proc_entry *this,
233 struct dsp_config *dsp,
234 unsigned int setting,
235 intptr_t value)
236{
237 switch (setting)
238 {
239 case DSP_PROC_INIT:
240 if (value == 0)
241 {
242 /* New object */
243 this->data = (intptr_t)&channel_mode_data;
244 this->process[1] = channel_mode_process_new_format;
245 ((struct channel_mode_data *)this->data)->dsp = dsp;
246 }
247
248 /* Force format change call each time */
249 this->process[0] = channel_mode_process_new_format;
250 dsp_proc_activate(dsp, DSP_PROC_CHANNEL_MODE, true);
251 break;
252
253 case DSP_PROC_CLOSE:
254 ((struct channel_mode_data *)this->data)->dsp = NULL;
255 break;
256 }
257
258 return 1;
259}
260
261/* Database entry */
262DSP_PROC_DB_ENTRY(
263 CHANNEL_MODE,
264 channel_mode_configure);