summaryrefslogtreecommitdiff
path: root/lib/rbcodec/dsp/dsp_sample_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/dsp/dsp_sample_output.c')
-rw-r--r--lib/rbcodec/dsp/dsp_sample_output.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/lib/rbcodec/dsp/dsp_sample_output.c b/lib/rbcodec/dsp/dsp_sample_output.c
new file mode 100644
index 0000000000..47fde0440c
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_sample_output.c
@@ -0,0 +1,214 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Miika Pekkarinen
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 "dsp_sample_io.h"
26#include "dsp-util.h"
27#include <string.h>
28
29#if 0
30#include <debug.h>
31#else
32#undef DEBUGF
33#define DEBUGF(...)
34#endif
35
36/* May be implemented in here or externally.*/
37void sample_output_mono(struct sample_io_data *this,
38 struct dsp_buffer *src, struct dsp_buffer *dst);
39void sample_output_stereo(struct sample_io_data *this,
40 struct dsp_buffer *src, struct dsp_buffer *dst);
41void sample_output_dithered(struct sample_io_data *this,
42 struct dsp_buffer *src, struct dsp_buffer *dst);
43
44/** Sample output **/
45
46#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
47/* write mono internal format to output format */
48void sample_output_mono(struct sample_io_data *this,
49 struct dsp_buffer *src, struct dsp_buffer *dst)
50{
51 int count = this->outcount;
52 const int32_t *s0 = src->p32[0];
53 int16_t *d = dst->p16out;
54 int scale = src->format.output_scale;
55 int32_t dc_bias = 1L << (scale - 1);
56
57 do
58 {
59 int32_t lr = clip_sample_16((*s0++ + dc_bias) >> scale);
60 *d++ = lr;
61 *d++ = lr;
62 }
63 while (--count > 0);
64}
65
66/* write stereo internal format to output format */
67void sample_output_stereo(struct sample_io_data *this,
68 struct dsp_buffer *src, struct dsp_buffer *dst)
69{
70 int count = this->outcount;
71 const int32_t *s0 = src->p32[0];
72 const int32_t *s1 = src->p32[1];
73 int16_t *d = dst->p16out;
74 int scale = src->format.output_scale;
75 int32_t dc_bias = 1L << (scale - 1);
76
77 do
78 {
79 *d++ = clip_sample_16((*s0++ + dc_bias) >> scale);
80 *d++ = clip_sample_16((*s1++ + dc_bias) >> scale);
81 }
82 while (--count > 0);
83}
84#endif /* CPU */
85
86/**
87 * The "dither" code to convert the 24-bit samples produced by libmad was
88 * taken from the coolplayer project - coolplayer.sourceforge.net
89 *
90 * This function handles mono and stereo outputs.
91 */
92static struct dither_data
93{
94 struct dither_state
95 {
96 long error[3]; /* 00h: error term history */
97 long random; /* 0ch: last random value */
98 } state[2]; /* 0=left, 1=right */
99 bool enabled; /* 20h: dithered output enabled */
100 /* 24h */
101} dither_data IBSS_ATTR;
102
103void sample_output_dithered(struct sample_io_data *this,
104 struct dsp_buffer *src, struct dsp_buffer *dst)
105{
106 int count = this->outcount;
107 int channels = src->format.num_channels;
108 int scale = src->format.output_scale;
109 int32_t dc_bias = 1L << (scale - 1); /* 1/2 bit of significance */
110 int32_t mask = (1L << scale) - 1; /* Mask of bits quantized away */
111
112 for (int ch = 0; ch < channels; ch++)
113 {
114 struct dither_state *dither = &dither_data.state[ch];
115
116 const int32_t *s = src->p32[ch];
117 int16_t *d = &dst->p16out[ch];
118
119 for (int i = 0; i < count; i++, s++, d += 2)
120 {
121 /* Noise shape and bias (for correct rounding later) */
122 int32_t sample = *s;
123
124 sample += dither->error[0] - dither->error[1] + dither->error[2];
125 dither->error[2] = dither->error[1];
126 dither->error[1] = dither->error[0] / 2;
127
128 int32_t output = sample + dc_bias;
129
130 /* Dither, highpass triangle PDF */
131 int32_t random = dither->random*0x0019660dL + 0x3c6ef35fL;
132 output += (random & mask) - (dither->random & mask);
133 dither->random = random;
134
135 /* Quantize sample to output range */
136 output >>= scale;
137
138 /* Error feedback of quantization */
139 dither->error[0] = sample - (output << scale);
140
141 /* Clip and store */
142 *d = clip_sample_16(output);
143 }
144 }
145
146 if (channels > 1)
147 return;
148
149 /* Have to duplicate left samples into the right channel since
150 output is interleaved stereo */
151 int16_t *d = dst->p16out;
152
153 do
154 {
155 int16_t s = *d++;
156 *d++ = s;
157 }
158 while (--count > 0);
159}
160
161/* Initialize the output function for settings and format */
162static void dsp_sample_output_format_change(struct sample_io_data *this,
163 struct dsp_buffer *src,
164 struct dsp_buffer *dst)
165{
166 static const sample_output_fn_type fns[2][2] =
167 {
168 { sample_output_mono, /* DC-biased quantizing */
169 sample_output_stereo },
170 { sample_output_dithered, /* Tri-PDF dithering */
171 sample_output_dithered },
172 };
173
174 struct sample_format *format = &src->format;
175 bool dither = dsp_get_id((void *)this) == CODEC_IDX_AUDIO &&
176 dither_data.enabled;
177 int channels = format->num_channels;
178
179 DSP_PRINT_FORMAT(DSP Output, -1, *format);
180
181 this->output_samples[0] = fns[dither ? 1 : 0][channels - 1];
182 format_change_ack(format); /* always ack, we're last */
183
184 /* The real function mustn't be called with no data */
185 if (this->outcount > 0)
186 this->output_samples[0](this, src, dst);
187}
188
189void dsp_sample_output_init(struct sample_io_data *this)
190{
191 this->output_samples[0] = sample_output_stereo;
192 this->output_samples[1] = dsp_sample_output_format_change;
193}
194
195/* Flush the dither history */
196void dsp_sample_output_flush(struct sample_io_data *this)
197{
198 if (dsp_get_id((void *)this) == CODEC_IDX_AUDIO)
199 memset(dither_data.state, 0, sizeof (dither_data.state));
200}
201
202/** Output settings **/
203
204/* Set the tri-pdf dithered output */
205void dsp_dither_enable(bool enable)
206{
207 if (enable == dither_data.enabled)
208 return;
209
210 struct sample_io_data *data = (void *)dsp_get_config(CODEC_IDX_AUDIO);
211 dsp_sample_output_flush(data);
212 dither_data.enabled = enable;
213 data->output_samples[0] = dsp_sample_output_format_change;
214}