1 /* $NetBSD: msm6258.c,v 1.15 2005/12/24 20:27:30 perry 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.15 2005/12/24 20:27:30 perry 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 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))) 158 return err; 159 m = dst->end - dst->start; 160 m = min(m, max_used); 161 d = dst->inp; 162 s = this->src->outp; 163 enc_src = this->src->param.encoding; 164 if (enc_src == AUDIO_ENCODING_SLINEAR_LE) { 165 while (dst->used < m && this->src->used >= 4) { 166 uint8_t f; 167 int16_t ss; 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; 173 #else 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]; 178 #endif 179 f |= pcm2adpcm_step(mc, ss) << 4; 180 *d = f; 181 d = audio_stream_add_inp(dst, d, 1); 182 s = audio_stream_add_outp(this->src, s, 2); 183 } 184 } else { 185 while (dst->used < m && this->src->used >= 4) { 186 uint8_t f; 187 int16_t ss; 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; 193 #else 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]; 198 #endif 199 f |= pcm2adpcm_step(mc, ss) << 4; 200 *d = f; 201 d = audio_stream_add_inp(dst, d, 1); 202 s = audio_stream_add_outp(this->src, s, 2); 203 } 204 } 205 dst->inp = d; 206 this->src->outp = s; 207 return 0; 208 } 209 210 DEFINE_FILTER(msm6258_linear8_to_adpcm) 211 { 212 stream_filter_t *this; 213 struct msm6258_codecvar *mc; 214 uint8_t *d; 215 const uint8_t *s; 216 int m, err, enc_src; 217 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))) 221 return err; 222 m = dst->end - dst->start; 223 m = min(m, max_used); 224 d = dst->inp; 225 s = this->src->outp; 226 enc_src = this->src->param.encoding; 227 if (enc_src == AUDIO_ENCODING_SLINEAR_LE) { 228 while (dst->used < m && this->src->used >= 4) { 229 uint8_t f; 230 int16_t ss; 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; 236 *d = f; 237 d = audio_stream_add_inp(dst, d, 1); 238 s = audio_stream_add_outp(this->src, s, 1); 239 } 240 } else { 241 while (dst->used < m && this->src->used >= 4) { 242 uint8_t f; 243 int16_t ss; 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; 249 *d = f; 250 d = audio_stream_add_inp(dst, d, 1); 251 s = audio_stream_add_outp(this->src, s, 1); 252 } 253 } 254 dst->inp = d; 255 this->src->outp = s; 256 return 0; 257 } 258 259 /* 260 * OkiADPCM -> signed 16bit linear PCM 261 */ 262 static inline int16_t 263 adpcm2pcm_step(struct msm6258_codecvar *mc, uint8_t b) 264 { 265 int estim = (int)mc->mc_estim; 266 267 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b]; 268 estim += adpcm_estimstep[b]; 269 270 if (estim < 0) 271 estim = 0; 272 else if (estim > 48) 273 estim = 48; 274 275 mc->mc_estim = estim; 276 277 return mc->mc_amp; 278 } 279 280 DEFINE_FILTER(msm6258_adpcm_to_slinear16) 281 { 282 stream_filter_t *this; 283 struct msm6258_codecvar *mc; 284 uint8_t *d; 285 const uint8_t *s; 286 int m, err, enc_dst; 287 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))) 292 return err; 293 m = (dst->end - dst->start) & ~3; 294 m = min(m, max_used); 295 d = dst->inp; 296 s = this->src->outp; 297 enc_dst = dst->param.encoding; 298 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) { 299 while (dst->used < m && this->src->used >= 1) { 300 uint8_t a; 301 int16_t s1, s2; 302 a = s[0]; 303 s1 = adpcm2pcm_step(mc, a & 0x0f); 304 s2 = adpcm2pcm_step(mc, a >> 4); 305 #if BYTE_ORDER == LITTLE_ENDIAN 306 *(int16_t*)d = s1; 307 d = audio_stream_add_inp(dst, d, 2); 308 *(int16_t*)d = s2; 309 #else 310 d[0] = s1; 311 d[1] = s1 >> 8; 312 d = audio_stream_add_inp(dst, d, 2); 313 d[0] = s2; 314 d[1] = s2 >> 8; 315 #endif 316 d = audio_stream_add_inp(dst, d, 2); 317 s = audio_stream_add_outp(this->src, s, 1); 318 } 319 } else { 320 while (dst->used < m && this->src->used >= 1) { 321 uint8_t a; 322 int16_t s1, s2; 323 a = s[0]; 324 s1 = adpcm2pcm_step(mc, a & 0x0f); 325 s2 = adpcm2pcm_step(mc, a >> 4); 326 #if BYTE_ORDER == BIG_ENDIAN 327 *(int16_t*)d = s1; 328 d = audio_stream_add_inp(dst, d, 2); 329 *(int16_t*)d = s2; 330 #else 331 d[1] = s1; 332 d[0] = s1 >> 8; 333 d = audio_stream_add_inp(dst, d, 2); 334 d[1] = s2; 335 d[0] = s2 >> 8; 336 #endif 337 d = audio_stream_add_inp(dst, d, 2); 338 s = audio_stream_add_outp(this->src, s, 1); 339 } 340 } 341 dst->inp = d; 342 this->src->outp = s; 343 return 0; 344 } 345 346 DEFINE_FILTER(msm6258_adpcm_to_linear8) 347 { 348 stream_filter_t *this; 349 struct msm6258_codecvar *mc; 350 uint8_t *d; 351 const uint8_t *s; 352 int m, err, enc_dst; 353 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))) 358 return err; 359 m = (dst->end - dst->start) & ~1; 360 m = min(m, max_used); 361 d = dst->inp; 362 s = this->src->outp; 363 enc_dst = dst->param.encoding; 364 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) { 365 while (dst->used < m && this->src->used >= 1) { 366 uint8_t a; 367 int16_t s1, s2; 368 a = s[0]; 369 s1 = adpcm2pcm_step(mc, a & 0x0f); 370 s2 = adpcm2pcm_step(mc, a >> 4); 371 d[0] = s1 / 266; 372 d = audio_stream_add_inp(dst, d, 1); 373 d[0] = s2 / 266; 374 d = audio_stream_add_inp(dst, d, 1); 375 s = audio_stream_add_outp(this->src, s, 1); 376 } 377 } else { 378 while (dst->used < m && this->src->used >= 1) { 379 uint8_t a; 380 int16_t s1, s2; 381 a = s[0]; 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); 389 } 390 } 391 dst->inp = d; 392 this->src->outp = s; 393 return 0; 394 } 395