1 /* $NetBSD: msm6258.c,v 1.14 2005/12/11 12:21:28 christos Exp $ */
4 * Copyright (c) 2001 Tetsuya Isaki. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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
31 * OKI MSM6258 ADPCM voice synthesizer codec.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: msm6258.c,v 1.14 2005/12/11 12:21:28 christos Exp $");
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>
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>
49 struct msm6258_codecvar
{
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);
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
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
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
79 static stream_filter_t
*
80 msm6258_factory(int (*fetch_to
)(stream_fetcher_t
*, audio_stream_t
*, int))
82 struct msm6258_codecvar
*this;
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
;
93 msm6258_dtor(struct stream_filter
*this)
100 * signed 16bit linear PCM -> OkiADPCM
102 static inline uint8_t
103 pcm2adpcm_step(struct msm6258_codecvar
*mc
, int16_t a
)
105 int estim
= (int)mc
->mc_estim
;
112 dl
= adpcm_estim
[estim
];
113 c
= (df
/ 16) * 8 / dl
;
115 b
= (unsigned char)(-c
) / 2;
118 b
= (unsigned char)(c
) / 2;
124 mc
->mc_amp
+= (short)(adpcm_estimindex
[(int)s
] * dl
);
125 estim
+= adpcm_estimstep
[b
];
131 mc
->mc_estim
= estim
;
135 #define DEFINE_FILTER(name) \
137 name##_fetch_to(stream_fetcher_t *, audio_stream_t *, int); \
139 name(struct audio_softc *sc, const audio_params_t *from, \
140 const audio_params_t *to) \
142 return msm6258_factory(name##_fetch_to); \
145 name##_fetch_to(stream_fetcher_t *self, audio_stream_t *dst, int max_used)
147 DEFINE_FILTER(msm6258_slinear16_to_adpcm
)
149 stream_filter_t
*this;
150 struct msm6258_codecvar
*mc
;
155 this = (stream_filter_t
*)self
;
156 mc
= (struct msm6258_codecvar
*)self
;
157 if ((err
= this->prev
->fetch_to(this->prev
, this->src
, max_used
* 4)))
159 m
= dst
->end
- dst
->start
;
160 m
= min(m
, max_used
);
163 enc_src
= this->src
->param
.encoding
;
164 if (enc_src
== AUDIO_ENCODING_SLINEAR_LE
) {
165 while (dst
->used
< m
&& this->src
->used
>= 4) {
168 #if BYTE_ORDER == LITTLE_ENDIAN
169 ss
= *(const int16_t*)s
;
170 s
= audio_stream_add_outp(this->src
, s
, 2);
171 f
= pcm2adpcm_step(mc
, ss
);
172 ss
= *(const int16_t*)s
;
174 ss
= (s
[1] << 8) | s
[0];
175 s
= audio_stream_add_outp(this->src
, s
, 2);
176 f
= pcm2adpcm_step(mc
, ss
);
177 ss
= (s
[1] << 8) | s
[0];
179 f
|= pcm2adpcm_step(mc
, ss
) << 4;
181 d
= audio_stream_add_inp(dst
, d
, 1);
182 s
= audio_stream_add_outp(this->src
, s
, 2);
185 while (dst
->used
< m
&& this->src
->used
>= 4) {
188 #if BYTE_ORDER == BIG_ENDIAN
189 ss
= *(const int16_t*)s
;
190 s
= audio_stream_add_outp(this->src
, s
, 2);
191 f
= pcm2adpcm_step(mc
, ss
);
192 ss
= *(const int16_t*)s
;
194 ss
= (s
[0] << 8) | s
[1];
195 s
= audio_stream_add_outp(this->src
, s
, 2);
196 f
= pcm2adpcm_step(mc
, ss
);
197 ss
= (s
[0] << 8) | s
[1];
199 f
|= pcm2adpcm_step(mc
, ss
) << 4;
201 d
= audio_stream_add_inp(dst
, d
, 1);
202 s
= audio_stream_add_outp(this->src
, s
, 2);
210 DEFINE_FILTER(msm6258_linear8_to_adpcm
)
212 stream_filter_t
*this;
213 struct msm6258_codecvar
*mc
;
218 this = (stream_filter_t
*)self
;
219 mc
= (struct msm6258_codecvar
*)self
;
220 if ((err
= this->prev
->fetch_to(this->prev
, this->src
, max_used
* 2)))
222 m
= dst
->end
- dst
->start
;
223 m
= min(m
, max_used
);
226 enc_src
= this->src
->param
.encoding
;
227 if (enc_src
== AUDIO_ENCODING_SLINEAR_LE
) {
228 while (dst
->used
< m
&& this->src
->used
>= 4) {
231 ss
= ((int16_t)s
[0]) * 256;
232 s
= audio_stream_add_outp(this->src
, s
, 1);
233 f
= pcm2adpcm_step(mc
, ss
);
234 ss
= ((int16_t)s
[0]) * 256;
235 f
|= pcm2adpcm_step(mc
, ss
) << 4;
237 d
= audio_stream_add_inp(dst
, d
, 1);
238 s
= audio_stream_add_outp(this->src
, s
, 1);
241 while (dst
->used
< m
&& this->src
->used
>= 4) {
244 ss
= ((int16_t)(s
[0] ^ 0x80)) * 256;
245 s
= audio_stream_add_outp(this->src
, s
, 1);
246 f
= pcm2adpcm_step(mc
, ss
);
247 ss
= ((int16_t)(s
[0] ^ 0x80)) * 256;
248 f
|= pcm2adpcm_step(mc
, ss
) << 4;
250 d
= audio_stream_add_inp(dst
, d
, 1);
251 s
= audio_stream_add_outp(this->src
, s
, 1);
260 * OkiADPCM -> signed 16bit linear PCM
262 static inline int16_t
263 adpcm2pcm_step(struct msm6258_codecvar
*mc
, uint8_t b
)
265 int estim
= (int)mc
->mc_estim
;
267 mc
->mc_amp
+= adpcm_estim
[estim
] * adpcm_estimindex
[b
];
268 estim
+= adpcm_estimstep
[b
];
275 mc
->mc_estim
= estim
;
280 DEFINE_FILTER(msm6258_adpcm_to_slinear16
)
282 stream_filter_t
*this;
283 struct msm6258_codecvar
*mc
;
288 this = (stream_filter_t
*)self
;
289 mc
= (struct msm6258_codecvar
*)self
;
290 max_used
= (max_used
+ 3) & ~3; /* round up multiple of 4 */
291 if ((err
= this->prev
->fetch_to(this->prev
, this->src
, max_used
/ 4)))
293 m
= (dst
->end
- dst
->start
) & ~3;
294 m
= min(m
, max_used
);
297 enc_dst
= dst
->param
.encoding
;
298 if (enc_dst
== AUDIO_ENCODING_SLINEAR_LE
) {
299 while (dst
->used
< m
&& this->src
->used
>= 1) {
303 s1
= adpcm2pcm_step(mc
, a
& 0x0f);
304 s2
= adpcm2pcm_step(mc
, a
>> 4);
305 #if BYTE_ORDER == LITTLE_ENDIAN
307 d
= audio_stream_add_inp(dst
, d
, 2);
312 d
= audio_stream_add_inp(dst
, d
, 2);
316 d
= audio_stream_add_inp(dst
, d
, 2);
317 s
= audio_stream_add_outp(this->src
, s
, 1);
320 while (dst
->used
< m
&& this->src
->used
>= 1) {
324 s1
= adpcm2pcm_step(mc
, a
& 0x0f);
325 s2
= adpcm2pcm_step(mc
, a
>> 4);
326 #if BYTE_ORDER == BIG_ENDIAN
328 d
= audio_stream_add_inp(dst
, d
, 2);
333 d
= audio_stream_add_inp(dst
, d
, 2);
337 d
= audio_stream_add_inp(dst
, d
, 2);
338 s
= audio_stream_add_outp(this->src
, s
, 1);
346 DEFINE_FILTER(msm6258_adpcm_to_linear8
)
348 stream_filter_t
*this;
349 struct msm6258_codecvar
*mc
;
354 this = (stream_filter_t
*)self
;
355 mc
= (struct msm6258_codecvar
*)self
;
356 max_used
= (max_used
+ 1) & ~1; /* round up multiple of 4 */
357 if ((err
= this->prev
->fetch_to(this->prev
, this->src
, max_used
/ 2)))
359 m
= (dst
->end
- dst
->start
) & ~1;
360 m
= min(m
, max_used
);
363 enc_dst
= dst
->param
.encoding
;
364 if (enc_dst
== AUDIO_ENCODING_SLINEAR_LE
) {
365 while (dst
->used
< m
&& this->src
->used
>= 1) {
369 s1
= adpcm2pcm_step(mc
, a
& 0x0f);
370 s2
= adpcm2pcm_step(mc
, a
>> 4);
372 d
= audio_stream_add_inp(dst
, d
, 1);
374 d
= audio_stream_add_inp(dst
, d
, 1);
375 s
= audio_stream_add_outp(this->src
, s
, 1);
378 while (dst
->used
< m
&& this->src
->used
>= 1) {
382 s1
= adpcm2pcm_step(mc
, a
& 0x0f);
383 s2
= adpcm2pcm_step(mc
, a
>> 4);
384 d
[0] = (s1
/ 266) ^ 0x80;
385 d
= audio_stream_add_inp(dst
, d
, 1);
386 d
[0] = (s2
/ 266) ^ 0x80;
387 d
= audio_stream_add_inp(dst
, d
, 1);
388 s
= audio_stream_add_outp(this->src
, s
, 1);