1 /* 2 * Attenuated route Plug-In 3 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> 4 * 5 * 6 * This library is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Library General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * 20 */ 21 22 #include <sound/driver.h> 23 #include <linux/slab.h> 24 #include <linux/time.h> 25 #include <sound/core.h> 26 #include <sound/pcm.h> 27 #include "pcm_plugin.h" 28 29 /* The best possible hack to support missing optimization in gcc 2.7.2.3 */ 30 #if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0 31 #define div(a) a /= ROUTE_PLUGIN_RESOLUTION 32 #elif ROUTE_PLUGIN_RESOLUTION == 16 33 #define div(a) a >>= 4 34 #else 35 #error "Add some code here" 36 #endif 37 38 struct ttable_dst; 39 40 typedef void (*route_channel_f)(struct snd_pcm_plugin *plugin, 41 const struct snd_pcm_plugin_channel *src_channels, 42 struct snd_pcm_plugin_channel *dst_channel, 43 struct ttable_dst *ttable, snd_pcm_uframes_t frames); 44 45 struct ttable_src { 46 int channel; 47 int as_int; 48 }; 49 50 struct ttable_dst { 51 int att; /* Attenuated */ 52 unsigned int nsrcs; 53 struct ttable_src *srcs; 54 route_channel_f func; 55 }; 56 57 struct route_priv { 58 enum {R_UINT32=0, R_UINT64=1} sum_type; 59 int get, put; 60 int conv; 61 int src_sample_size; 62 struct ttable_dst ttable[0]; 63 }; 64 65 union sum { 66 u_int32_t as_uint32; 67 u_int64_t as_uint64; 68 }; 69 70 71 static void route_to_channel_from_zero(struct snd_pcm_plugin *plugin, 72 const struct snd_pcm_plugin_channel *src_channels, 73 struct snd_pcm_plugin_channel *dst_channel, 74 struct ttable_dst *ttable, 75 snd_pcm_uframes_t frames) 76 { 77 if (dst_channel->wanted) 78 snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format); 79 dst_channel->enabled = 0; 80 } 81 82 static void route_to_channel_from_one(struct snd_pcm_plugin *plugin, 83 const struct snd_pcm_plugin_channel *src_channels, 84 struct snd_pcm_plugin_channel *dst_channel, 85 struct ttable_dst *ttable, 86 snd_pcm_uframes_t frames) 87 { 88 #define CONV_LABELS 89 #include "plugin_ops.h" 90 #undef CONV_LABELS 91 struct route_priv *data = (struct route_priv *)plugin->extra_data; 92 void *conv; 93 const struct snd_pcm_plugin_channel *src_channel = NULL; 94 unsigned int srcidx; 95 char *src, *dst; 96 int src_step, dst_step; 97 for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { 98 src_channel = &src_channels[ttable->srcs[srcidx].channel]; 99 if (src_channel->area.addr != NULL) 100 break; 101 } 102 if (srcidx == ttable->nsrcs) { 103 route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); 104 return; 105 } 106 107 dst_channel->enabled = 1; 108 conv = conv_labels[data->conv]; 109 src = src_channel->area.addr + src_channel->area.first / 8; 110 src_step = src_channel->area.step / 8; 111 dst = dst_channel->area.addr + dst_channel->area.first / 8; 112 dst_step = dst_channel->area.step / 8; 113 while (frames-- > 0) { 114 goto *conv; 115 #define CONV_END after 116 #include "plugin_ops.h" 117 #undef CONV_END 118 after: 119 src += src_step; 120 dst += dst_step; 121 } 122 } 123 124 static void route_to_channel(struct snd_pcm_plugin *plugin, 125 const struct snd_pcm_plugin_channel *src_channels, 126 struct snd_pcm_plugin_channel *dst_channel, 127 struct ttable_dst *ttable, snd_pcm_uframes_t frames) 128 { 129 #define GET_U_LABELS 130 #define PUT_U32_LABELS 131 #include "plugin_ops.h" 132 #undef GET_U_LABELS 133 #undef PUT_U32_LABELS 134 static void *zero_labels[2] = { &&zero_int32, &&zero_int64 }; 135 /* sum_type att */ 136 static void *add_labels[2 * 2] = { &&add_int32_noatt, &&add_int32_att, 137 &&add_int64_noatt, &&add_int64_att, 138 }; 139 /* sum_type att shift */ 140 static void *norm_labels[2 * 2 * 4] = { NULL, 141 &&norm_int32_8_noatt, 142 &&norm_int32_16_noatt, 143 &&norm_int32_24_noatt, 144 NULL, 145 &&norm_int32_8_att, 146 &&norm_int32_16_att, 147 &&norm_int32_24_att, 148 &&norm_int64_0_noatt, 149 &&norm_int64_8_noatt, 150 &&norm_int64_16_noatt, 151 &&norm_int64_24_noatt, 152 &&norm_int64_0_att, 153 &&norm_int64_8_att, 154 &&norm_int64_16_att, 155 &&norm_int64_24_att, 156 }; 157 struct route_priv *data = (struct route_priv *)plugin->extra_data; 158 void *zero, *get, *add, *norm, *put_u32; 159 int nsrcs = ttable->nsrcs; 160 char *dst; 161 int dst_step; 162 char *srcs[nsrcs]; 163 int src_steps[nsrcs]; 164 struct ttable_src src_tt[nsrcs]; 165 u_int32_t sample = 0; 166 int srcidx, srcidx1 = 0; 167 for (srcidx = 0; srcidx < nsrcs; ++srcidx) { 168 const struct snd_pcm_plugin_channel *src_channel = &src_channels[ttable->srcs[srcidx].channel]; 169 if (!src_channel->enabled) 170 continue; 171 srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8; 172 src_steps[srcidx1] = src_channel->area.step / 8; 173 src_tt[srcidx1] = ttable->srcs[srcidx]; 174 srcidx1++; 175 } 176 nsrcs = srcidx1; 177 if (nsrcs == 0) { 178 route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); 179 return; 180 } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) { 181 route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames); 182 return; 183 } 184 185 dst_channel->enabled = 1; 186 zero = zero_labels[data->sum_type]; 187 get = get_u_labels[data->get]; 188 add = add_labels[data->sum_type * 2 + ttable->att]; 189 norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size]; 190 put_u32 = put_u32_labels[data->put]; 191 dst = dst_channel->area.addr + dst_channel->area.first / 8; 192 dst_step = dst_channel->area.step / 8; 193 194 while (frames-- > 0) { 195 struct ttable_src *ttp = src_tt; 196 union sum sum; 197 198 /* Zero sum */ 199 goto *zero; 200 zero_int32: 201 sum.as_uint32 = 0; 202 goto zero_end; 203 zero_int64: 204 sum.as_uint64 = 0; 205 goto zero_end; 206 zero_end: 207 for (srcidx = 0; srcidx < nsrcs; ++srcidx) { 208 char *src = srcs[srcidx]; 209 210 /* Get sample */ 211 goto *get; 212 #define GET_U_END after_get 213 #include "plugin_ops.h" 214 #undef GET_U_END 215 after_get: 216 217 /* Sum */ 218 goto *add; 219 add_int32_att: 220 sum.as_uint32 += sample * ttp->as_int; 221 goto after_sum; 222 add_int32_noatt: 223 if (ttp->as_int) 224 sum.as_uint32 += sample; 225 goto after_sum; 226 add_int64_att: 227 sum.as_uint64 += (u_int64_t) sample * ttp->as_int; 228 goto after_sum; 229 add_int64_noatt: 230 if (ttp->as_int) 231 sum.as_uint64 += sample; 232 goto after_sum; 233 after_sum: 234 srcs[srcidx] += src_steps[srcidx]; 235 ttp++; 236 } 237 238 /* Normalization */ 239 goto *norm; 240 norm_int32_8_att: 241 sum.as_uint64 = sum.as_uint32; 242 norm_int64_8_att: 243 sum.as_uint64 <<= 8; 244 norm_int64_0_att: 245 div(sum.as_uint64); 246 goto norm_int; 247 248 norm_int32_16_att: 249 sum.as_uint64 = sum.as_uint32; 250 norm_int64_16_att: 251 sum.as_uint64 <<= 16; 252 div(sum.as_uint64); 253 goto norm_int; 254 255 norm_int32_24_att: 256 sum.as_uint64 = sum.as_uint32; 257 norm_int64_24_att: 258 sum.as_uint64 <<= 24; 259 div(sum.as_uint64); 260 goto norm_int; 261 262 norm_int32_8_noatt: 263 sum.as_uint64 = sum.as_uint32; 264 norm_int64_8_noatt: 265 sum.as_uint64 <<= 8; 266 goto norm_int; 267 268 norm_int32_16_noatt: 269 sum.as_uint64 = sum.as_uint32; 270 norm_int64_16_noatt: 271 sum.as_uint64 <<= 16; 272 goto norm_int; 273 274 norm_int32_24_noatt: 275 sum.as_uint64 = sum.as_uint32; 276 norm_int64_24_noatt: 277 sum.as_uint64 <<= 24; 278 goto norm_int; 279 280 norm_int64_0_noatt: 281 norm_int: 282 if (sum.as_uint64 > (u_int32_t)0xffffffff) 283 sample = (u_int32_t)0xffffffff; 284 else 285 sample = sum.as_uint64; 286 goto after_norm; 287 288 after_norm: 289 290 /* Put sample */ 291 goto *put_u32; 292 #define PUT_U32_END after_put_u32 293 #include "plugin_ops.h" 294 #undef PUT_U32_END 295 after_put_u32: 296 297 dst += dst_step; 298 } 299 } 300 301 static int route_src_channels_mask(struct snd_pcm_plugin *plugin, 302 unsigned long *dst_vmask, 303 unsigned long **src_vmask) 304 { 305 struct route_priv *data = (struct route_priv *)plugin->extra_data; 306 int schannels = plugin->src_format.channels; 307 int dchannels = plugin->dst_format.channels; 308 unsigned long *vmask = plugin->src_vmask; 309 int channel; 310 struct ttable_dst *dp = data->ttable; 311 bitmap_zero(vmask, schannels); 312 for (channel = 0; channel < dchannels; channel++, dp++) { 313 unsigned int src; 314 struct ttable_src *sp; 315 if (!test_bit(channel, dst_vmask)) 316 continue; 317 sp = dp->srcs; 318 for (src = 0; src < dp->nsrcs; src++, sp++) 319 set_bit(sp->channel, vmask); 320 } 321 *src_vmask = vmask; 322 return 0; 323 } 324 325 static int route_dst_channels_mask(struct snd_pcm_plugin *plugin, 326 unsigned long *src_vmask, 327 unsigned long **dst_vmask) 328 { 329 struct route_priv *data = (struct route_priv *)plugin->extra_data; 330 int dchannels = plugin->dst_format.channels; 331 unsigned long *vmask = plugin->dst_vmask; 332 int channel; 333 struct ttable_dst *dp = data->ttable; 334 bitmap_zero(vmask, dchannels); 335 for (channel = 0; channel < dchannels; channel++, dp++) { 336 unsigned int src; 337 struct ttable_src *sp; 338 sp = dp->srcs; 339 for (src = 0; src < dp->nsrcs; src++, sp++) { 340 if (test_bit(sp->channel, src_vmask)) { 341 set_bit(channel, vmask); 342 break; 343 } 344 } 345 } 346 *dst_vmask = vmask; 347 return 0; 348 } 349 350 static void route_free(struct snd_pcm_plugin *plugin) 351 { 352 struct route_priv *data = (struct route_priv *)plugin->extra_data; 353 unsigned int dst_channel; 354 for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { 355 kfree(data->ttable[dst_channel].srcs); 356 } 357 } 358 359 static int route_load_ttable(struct snd_pcm_plugin *plugin, 360 const int *src_ttable) 361 { 362 struct route_priv *data; 363 unsigned int src_channel, dst_channel; 364 const int *sptr; 365 struct ttable_dst *dptr; 366 if (src_ttable == NULL) 367 return 0; 368 data = (struct route_priv *)plugin->extra_data; 369 dptr = data->ttable; 370 sptr = src_ttable; 371 plugin->private_free = route_free; 372 for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { 373 int t = 0; 374 int att = 0; 375 int nsrcs = 0; 376 struct ttable_src srcs[plugin->src_format.channels]; 377 for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) { 378 snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO); 379 if (*sptr != 0) { 380 srcs[nsrcs].channel = src_channel; 381 srcs[nsrcs].as_int = *sptr; 382 if (*sptr != FULL) 383 att = 1; 384 t += *sptr; 385 nsrcs++; 386 } 387 sptr++; 388 } 389 dptr->att = att; 390 dptr->nsrcs = nsrcs; 391 if (nsrcs == 0) 392 dptr->func = route_to_channel_from_zero; 393 else if (nsrcs == 1 && !att) 394 dptr->func = route_to_channel_from_one; 395 else 396 dptr->func = route_to_channel; 397 if (nsrcs > 0) { 398 int srcidx; 399 dptr->srcs = kcalloc(nsrcs, sizeof(*srcs), GFP_KERNEL); 400 for(srcidx = 0; srcidx < nsrcs; srcidx++) 401 dptr->srcs[srcidx] = srcs[srcidx]; 402 } else 403 dptr->srcs = NULL; 404 dptr++; 405 } 406 return 0; 407 } 408 409 static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin, 410 const struct snd_pcm_plugin_channel *src_channels, 411 struct snd_pcm_plugin_channel *dst_channels, 412 snd_pcm_uframes_t frames) 413 { 414 struct route_priv *data; 415 int src_nchannels, dst_nchannels; 416 int dst_channel; 417 struct ttable_dst *ttp; 418 struct snd_pcm_plugin_channel *dvp; 419 420 snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); 421 if (frames == 0) 422 return 0; 423 data = (struct route_priv *)plugin->extra_data; 424 425 src_nchannels = plugin->src_format.channels; 426 dst_nchannels = plugin->dst_format.channels; 427 428 #ifdef CONFIG_SND_DEBUG 429 { 430 int src_channel; 431 for (src_channel = 0; src_channel < src_nchannels; ++src_channel) { 432 snd_assert(src_channels[src_channel].area.first % 8 == 0 || 433 src_channels[src_channel].area.step % 8 == 0, 434 return -ENXIO); 435 } 436 for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { 437 snd_assert(dst_channels[dst_channel].area.first % 8 == 0 || 438 dst_channels[dst_channel].area.step % 8 == 0, 439 return -ENXIO); 440 } 441 } 442 #endif 443 444 ttp = data->ttable; 445 dvp = dst_channels; 446 for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { 447 ttp->func(plugin, src_channels, dvp, ttp, frames); 448 dvp++; 449 ttp++; 450 } 451 return frames; 452 } 453 454 int getput_index(int format) 455 { 456 int sign, width, endian; 457 sign = !snd_pcm_format_signed(format); 458 width = snd_pcm_format_width(format) / 8 - 1; 459 if (width < 0 || width > 3) { 460 snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format); 461 width = 0; 462 } 463 #ifdef SNDRV_LITTLE_ENDIAN 464 endian = snd_pcm_format_big_endian(format); 465 #else 466 endian = snd_pcm_format_little_endian(format); 467 #endif 468 if (endian < 0) 469 endian = 0; 470 return width * 4 + endian * 2 + sign; 471 } 472 473 int snd_pcm_plugin_build_route(struct snd_pcm_substream *plug, 474 struct snd_pcm_plugin_format *src_format, 475 struct snd_pcm_plugin_format *dst_format, 476 int *ttable, 477 struct snd_pcm_plugin **r_plugin) 478 { 479 struct route_priv *data; 480 struct snd_pcm_plugin *plugin; 481 int err; 482 483 snd_assert(r_plugin != NULL, return -ENXIO); 484 *r_plugin = NULL; 485 snd_assert(src_format->rate == dst_format->rate, return -ENXIO); 486 snd_assert(snd_pcm_format_linear(src_format->format) != 0 && 487 snd_pcm_format_linear(dst_format->format) != 0, 488 return -ENXIO); 489 490 err = snd_pcm_plugin_build(plug, "attenuated route conversion", 491 src_format, dst_format, 492 sizeof(struct route_priv) + 493 sizeof(data->ttable[0]) * dst_format->channels, 494 &plugin); 495 if (err < 0) 496 return err; 497 498 data = (struct route_priv *)plugin->extra_data; 499 500 data->get = getput_index(src_format->format); 501 snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL); 502 data->put = getput_index(dst_format->format); 503 snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL); 504 data->conv = conv_index(src_format->format, dst_format->format); 505 506 if (snd_pcm_format_width(src_format->format) == 32) 507 data->sum_type = R_UINT64; 508 else 509 data->sum_type = R_UINT32; 510 data->src_sample_size = snd_pcm_format_width(src_format->format) / 8; 511 512 if ((err = route_load_ttable(plugin, ttable)) < 0) { 513 snd_pcm_plugin_free(plugin); 514 return err; 515 } 516 plugin->transfer = route_transfer; 517 plugin->src_channels_mask = route_src_channels_mask; 518 plugin->dst_channels_mask = route_dst_channels_mask; 519 *r_plugin = plugin; 520 return 0; 521 } 522