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