msm6258.c revision 1.11.4.1 1 /* $NetBSD: msm6258.c,v 1.11.4.1 2005/01/03 16:47:12 kent Exp $ */
2
3 /*
4 * Copyright (c) 2001 Tetsuya Isaki. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * OKI MSM6258 ADPCM voice synthesizer codec.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: msm6258.c,v 1.11.4.1 2005/01/03 16:47:12 kent Exp $");
36
37 #include <sys/systm.h>
38 #include <sys/device.h>
39 #include <sys/malloc.h>
40 #include <sys/select.h>
41 #include <sys/audioio.h>
42
43 #include <dev/audio_if.h>
44 #include <dev/auconv.h>
45 #include <dev/audiovar.h>
46 #include <dev/mulaw.h>
47 #include <dev/ic/msm6258var.h>
48
49 struct msm6258_codecvar {
50 stream_filter_t base;
51 short mc_amp;
52 char mc_estim;
53 };
54
55 static stream_filter_t *msm6258_factory
56 (int (*)(stream_fetcher_t *, audio_stream_t *, int));
57 static void msm6258_dtor(struct stream_filter *);
58 static __inline uint8_t pcm2adpcm_step(struct msm6258_codecvar *, int16_t);
59 static __inline int16_t adpcm2pcm_step(struct msm6258_codecvar *, uint8_t);
60
61 static const int adpcm_estimindex[16] = {
62 2, 6, 10, 14, 18, 22, 26, 30,
63 -2, -6, -10, -14, -18, -22, -26, -30
64 };
65
66 static const int adpcm_estim[49] = {
67 16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
68 41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
69 107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
70 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
71 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
72 };
73
74 static const int adpcm_estimstep[16] = {
75 -1, -1, -1, -1, 2, 4, 6, 8,
76 -1, -1, -1, -1, 2, 4, 6, 8
77 };
78
79 static stream_filter_t *
80 msm6258_factory(int (*fetch_to)(stream_fetcher_t *, audio_stream_t *, int))
81 {
82 struct msm6258_codecvar *this;
83
84 this = malloc(sizeof(*this), M_DEVBUF, M_WAITOK | M_ZERO);
85 this->base.base.fetch_to = fetch_to;
86 this->base.dtor = msm6258_dtor;
87 this->base.set_fetcher = stream_filter_set_fetcher;
88 this->base.set_inputbuffer = stream_filter_set_inputbuffer;
89 return &this->base;
90 }
91
92 static void
93 msm6258_dtor(struct stream_filter *this)
94 {
95 if (this != NULL)
96 free(this, M_DEVBUF);
97 }
98
99 /*
100 * signed 16bit linear PCM -> OkiADPCM
101 */
102 static __inline uint8_t
103 pcm2adpcm_step(struct msm6258_codecvar *mc, int16_t a)
104 {
105 int estim = (int)mc->mc_estim;
106 int df;
107 short dl, c;
108 uint8_t b;
109 uint8_t s;
110
111 df = a - mc->mc_amp;
112 dl = adpcm_estim[estim];
113 c = (df / 16) * 8 / dl;
114 if (df < 0) {
115 b = (unsigned char)(-c) / 2;
116 s = 0x08;
117 } else {
118 b = (unsigned char)(c) / 2;
119 s = 0;
120 }
121 if (b > 7)
122 b = 7;
123 s |= b;
124 mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl);
125 estim += adpcm_estimstep[b];
126 if (estim < 0)
127 estim = 0;
128 else if (estim > 48)
129 estim = 48;
130
131 mc->mc_estim = estim;
132 return s;
133 }
134
135 #define DEFINE_FILTER(name) \
136 static int \
137 name##_fetch_to(stream_fetcher_t *, audio_stream_t *, int); \
138 stream_filter_t * \
139 name(struct audio_softc *sc, const audio_params_t *from, \
140 const audio_params_t *to) \
141 { \
142 return msm6258_factory(name##_fetch_to); \
143 } \
144 static int \
145 name##_fetch_to(stream_fetcher_t *self, audio_stream_t *dst, int max_used)
146
147 DEFINE_FILTER(msm6258_slinear16_to_adpcm)
148 {
149 stream_filter_t *this;
150 struct msm6258_codecvar *mc;
151 uint8_t *d;
152 const uint8_t *s;
153 int m, err, enc_src;
154 int used_dst, used_src;
155
156 this = (stream_filter_t *)self;
157 mc = (struct msm6258_codecvar *)self;
158 if ((err = this->prev->fetch_to(this->prev, this->src, max_used * 4)))
159 return err;
160 m = dst->end - dst->start;
161 m = min(m, max_used);
162 d = dst->inp;
163 s = this->src->outp;
164 used_dst = audio_stream_get_used(dst);
165 used_src = audio_stream_get_used(this->src);
166 enc_src = this->src->param.encoding;
167 if (enc_src == AUDIO_ENCODING_SLINEAR_LE) {
168 while (used_dst < m && used_src >= 4) {
169 uint8_t f;
170 int16_t ss;
171 #if BYTE_ORDER == LITTLE_ENDIAN
172 ss = *(int16_t*)s;
173 f = pcm2adpcm_step(mc, ss);
174 ss = *(int16_t*)(s + 2);
175 #else
176 ss = (s[1] << 8) | s[0];
177 f = pcm2adpcm_step(mc, ss);
178 ss = (s[3] << 8) | s[2];
179 #endif
180 f |= pcm2adpcm_step(mc, ss) << 4;
181 *d = f;
182 d = audio_stream_add_inp(dst, d, 1);
183 s = audio_stream_add_outp(this->src, s, 4);
184 used_dst += 1;
185 used_src -= 4;
186 }
187 } else {
188 while (used_dst < m && used_src >= 4) {
189 uint8_t f;
190 int16_t ss;
191 #if BYTE_ORDER == BIG_ENDIAN
192 ss = *(int16_t*)s;
193 f = pcm2adpcm_step(mc, ss);
194 ss = *(int16_t*)(s + 2);
195 #else
196 ss = (s[0] << 8) | s[1];
197 f = pcm2adpcm_step(mc, ss);
198 ss = (s[2] << 8) | s[3];
199 #endif
200 f |= pcm2adpcm_step(mc, ss) << 4;
201 *d = f;
202 d = audio_stream_add_inp(dst, d, 1);
203 s = audio_stream_add_outp(this->src, s, 4);
204 used_dst += 1;
205 used_src -= 4;
206 }
207 }
208 dst->inp = d;
209 this->src->outp = s;
210 return 0;
211 }
212
213 DEFINE_FILTER(msm6258_linear8_to_adpcm)
214 {
215 stream_filter_t *this;
216 struct msm6258_codecvar *mc;
217 uint8_t *d;
218 const uint8_t *s;
219 int m, err, enc_src;
220 int used_dst, used_src;
221
222 this = (stream_filter_t *)self;
223 mc = (struct msm6258_codecvar *)self;
224 if ((err = this->prev->fetch_to(this->prev, this->src, max_used * 2)))
225 return err;
226 m = dst->end - dst->start;
227 m = min(m, max_used);
228 d = dst->inp;
229 s = this->src->outp;
230 used_dst = audio_stream_get_used(dst);
231 used_src = audio_stream_get_used(this->src);
232 enc_src = this->src->param.encoding;
233 if (enc_src == AUDIO_ENCODING_SLINEAR_LE) {
234 while (used_dst < m && used_src >= 4) {
235 uint8_t f;
236 int16_t ss;
237 ss = ((int16_t)s[0]) * 256;
238 f = pcm2adpcm_step(mc, ss);
239 ss = ((int16_t)s[1]) * 256;
240 f |= pcm2adpcm_step(mc, ss) << 4;
241 *d = f;
242 d = audio_stream_add_inp(dst, d, 1);
243 s = audio_stream_add_outp(this->src, s, 2);
244 used_dst += 1;
245 used_src -= 2;
246 }
247 } else {
248 while (used_dst < m && used_src >= 4) {
249 uint8_t f;
250 int16_t ss;
251 ss = ((int16_t)(s[0] ^ 0x80)) * 256;
252 f = pcm2adpcm_step(mc, ss);
253 ss = ((int16_t)(s[1] ^ 0x80)) * 256;
254 f |= pcm2adpcm_step(mc, ss) << 4;
255 *d = f;
256 d = audio_stream_add_inp(dst, d, 1);
257 s = audio_stream_add_outp(this->src, s, 2);
258 used_dst += 1;
259 used_src -= 2;
260 }
261 }
262 dst->inp = d;
263 this->src->outp = s;
264 return 0;
265 }
266
267 /*
268 * OkiADPCM -> signed 16bit linear PCM
269 */
270 static __inline int16_t
271 adpcm2pcm_step(struct msm6258_codecvar *mc, uint8_t b)
272 {
273 int estim = (int)mc->mc_estim;
274
275 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b];
276 estim += adpcm_estimstep[b];
277
278 if (estim < 0)
279 estim = 0;
280 else if (estim > 48)
281 estim = 48;
282
283 mc->mc_estim = estim;
284
285 return mc->mc_amp;
286 }
287
288 DEFINE_FILTER(msm6258_adpcm_to_slinear16)
289 {
290 stream_filter_t *this;
291 struct msm6258_codecvar *mc;
292 uint8_t *d;
293 const uint8_t *s;
294 int m, err, enc_dst;
295 int used_dst, used_src;
296
297 this = (stream_filter_t *)self;
298 mc = (struct msm6258_codecvar *)self;
299 max_used = (max_used + 3) & ~3; /* round up multiple of 4 */
300 if ((err = this->prev->fetch_to(this->prev, this->src, max_used / 4)))
301 return err;
302 m = (dst->end - dst->start) & ~3;
303 m = min(m, max_used);
304 d = dst->inp;
305 s = this->src->outp;
306 used_dst = audio_stream_get_used(dst);
307 used_src = audio_stream_get_used(this->src);
308 enc_dst = dst->param.encoding;
309 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) {
310 while (used_dst < m && used_src >= 1) {
311 uint8_t a;
312 int16_t s1, s2;
313 a = s[0];
314 s1 = adpcm2pcm_step(mc, a & 0x0f);
315 s2 = adpcm2pcm_step(mc, a >> 4);
316 #if BYTE_ORDER == LITTLE_ENDIAN
317 *(int16_t*)d = s1;
318 *(int16_t*)(d + 2) = s2;
319 #else
320 d[0] = s1;
321 d[1] = s1 >> 8;
322 d[2] = s2;
323 d[3] = s2 >> 8;
324 #endif
325 d = audio_stream_add_inp(dst, d, 4);
326 s = audio_stream_add_outp(this->src, s, 1);
327 used_dst += 4;
328 used_src -= 1;
329 }
330 } else {
331 while (used_dst < m && used_src >= 1) {
332 uint8_t a;
333 int16_t s1, s2;
334 a = s[0];
335 s1 = adpcm2pcm_step(mc, a & 0x0f);
336 s2 = adpcm2pcm_step(mc, a >> 4);
337 #if BYTE_ORDER == BIG_ENDIAN
338 *(int16_t*)d = s1;
339 *(int16_t*)(d + 2) = s2;
340 #else
341 d[1] = s1;
342 d[0] = s1 >> 8;
343 d[3] = s2;
344 d[2] = s2 >> 8;
345 #endif
346 d = audio_stream_add_inp(dst, d, 4);
347 s = audio_stream_add_outp(this->src, s, 1);
348 used_dst += 4;
349 used_src -= 1;
350 }
351 }
352 dst->inp = d;
353 this->src->outp = s;
354 return 0;
355 }
356
357 DEFINE_FILTER(msm6258_adpcm_to_linear8)
358 {
359 stream_filter_t *this;
360 struct msm6258_codecvar *mc;
361 uint8_t *d;
362 const uint8_t *s;
363 int m, err, enc_dst;
364 int used_dst, used_src;
365
366 this = (stream_filter_t *)self;
367 mc = (struct msm6258_codecvar *)self;
368 max_used = (max_used + 1) & ~1; /* round up multiple of 4 */
369 if ((err = this->prev->fetch_to(this->prev, this->src, max_used / 2)))
370 return err;
371 m = (dst->end - dst->start) & ~1;
372 m = min(m, max_used);
373 d = dst->inp;
374 s = this->src->outp;
375 used_dst = audio_stream_get_used(dst);
376 used_src = audio_stream_get_used(this->src);
377 enc_dst = dst->param.encoding;
378 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) {
379 while (used_dst < m && used_src >= 1) {
380 uint8_t a;
381 int16_t s1, s2;
382 a = s[0];
383 s1 = adpcm2pcm_step(mc, a & 0x0f);
384 s2 = adpcm2pcm_step(mc, a >> 4);
385 d[0] = s1 / 266;
386 d[1] = s2 / 266;
387 d = audio_stream_add_inp(dst, d, 2);
388 s = audio_stream_add_outp(this->src, s, 1);
389 used_dst += 2;
390 used_src -= 1;
391 }
392 } else {
393 while (used_dst < m && used_src >= 1) {
394 uint8_t a;
395 int16_t s1, s2;
396 a = s[0];
397 s1 = adpcm2pcm_step(mc, a & 0x0f);
398 s2 = adpcm2pcm_step(mc, a >> 4);
399 d[0] = (s1 / 266) ^ 0x80;
400 d[1] = (s2 / 266) ^ 0x80;
401 d = audio_stream_add_inp(dst, d, 2);
402 s = audio_stream_add_outp(this->src, s, 1);
403 used_dst += 2;
404 used_src -= 1;
405 }
406 }
407 dst->inp = d;
408 this->src->outp = s;
409 return 0;
410 }
411