diff options
Diffstat (limited to 'lib/rbcodec/dsp/dsp_sample_output.c')
-rw-r--r-- | lib/rbcodec/dsp/dsp_sample_output.c | 214 |
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.*/ | ||
37 | void sample_output_mono(struct sample_io_data *this, | ||
38 | struct dsp_buffer *src, struct dsp_buffer *dst); | ||
39 | void sample_output_stereo(struct sample_io_data *this, | ||
40 | struct dsp_buffer *src, struct dsp_buffer *dst); | ||
41 | void 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 */ | ||
48 | void 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 */ | ||
67 | void 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 | */ | ||
92 | static 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 | |||
103 | void 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 */ | ||
162 | static 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 | |||
189 | void 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 */ | ||
196 | void 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 */ | ||
205 | void 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 | } | ||