1 /** 2 * \file pcm/pcm_lfloat.c 3 * \ingroup PCM_Plugins 4 * \brief PCM Linear<->Float Conversion Plugin Interface 5 * \author Jaroslav Kysela <perex (at) perex.cz> 6 * \date 2001 7 */ 8 /* 9 * PCM - Linear Integer <-> Linear Float conversion 10 * Copyright (c) 2001 by Jaroslav Kysela <perex (at) perex.cz> 11 * 12 * 13 * This library is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU Lesser General Public License as 15 * published by the Free Software Foundation; either version 2.1 of 16 * the License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU Lesser General Public License for more details. 22 * 23 * You should have received a copy of the GNU Lesser General Public 24 * License along with this library; if not, write to the Free Software 25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 * 27 */ 28 29 #include <byteswap.h> 30 #include "pcm_local.h" 31 #include "pcm_plugin.h" 32 33 #include "plugin_ops.h" 34 35 #ifndef DOC_HIDDEN 36 37 typedef float float_t; 38 typedef double double_t; 39 40 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ <= 91) 41 #define BUGGY_GCC 42 #endif 43 44 #ifndef PIC 45 /* entry for static linking */ 46 const char *_snd_module_pcm_lfloat = ""; 47 #endif 48 49 typedef struct { 50 /* This field need to be the first */ 51 snd_pcm_plugin_t plug; 52 unsigned int int32_idx; 53 unsigned int float32_idx; 54 snd_pcm_format_t sformat; 55 void (*func)(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, 56 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, 57 unsigned int channels, snd_pcm_uframes_t frames, 58 unsigned int get32idx, unsigned int put32floatidx); 59 } snd_pcm_lfloat_t; 60 61 int snd_pcm_lfloat_get_s32_index(snd_pcm_format_t format) 62 { 63 int width, endian; 64 65 switch (format) { 66 case SND_PCM_FORMAT_FLOAT_LE: 67 case SND_PCM_FORMAT_FLOAT_BE: 68 width = 32; 69 break; 70 case SND_PCM_FORMAT_FLOAT64_LE: 71 case SND_PCM_FORMAT_FLOAT64_BE: 72 width = 64; 73 break; 74 default: 75 return -EINVAL; 76 } 77 #ifdef SND_LITTLE_ENDIAN 78 endian = snd_pcm_format_big_endian(format); 79 #else 80 endian = snd_pcm_format_little_endian(format); 81 #endif 82 return ((width / 32)-1) * 2 + endian; 83 } 84 85 int snd_pcm_lfloat_put_s32_index(snd_pcm_format_t format) 86 { 87 return snd_pcm_lfloat_get_s32_index(format); 88 } 89 90 #endif /* DOC_HIDDEN */ 91 92 #ifndef BUGGY_GCC 93 94 #ifndef DOC_HIDDEN 95 96 void snd_pcm_lfloat_convert_integer_float(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, 97 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, 98 unsigned int channels, snd_pcm_uframes_t frames, 99 unsigned int get32idx, unsigned int put32floatidx) 100 { 101 #define GET32_LABELS 102 #define PUT32F_LABELS 103 #include "plugin_ops.h" 104 #undef PUT32F_LABELS 105 #undef GET32_LABELS 106 void *get32 = get32_labels[get32idx]; 107 void *put32float = put32float_labels[put32floatidx]; 108 unsigned int channel; 109 for (channel = 0; channel < channels; ++channel) { 110 const char *src; 111 char *dst; 112 int src_step, dst_step; 113 snd_pcm_uframes_t frames1; 114 int32_t sample = 0; 115 snd_tmp_float_t tmp_float; 116 snd_tmp_double_t tmp_double; 117 const snd_pcm_channel_area_t *src_area = &src_areas[channel]; 118 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; 119 src = snd_pcm_channel_area_addr(src_area, src_offset); 120 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); 121 src_step = snd_pcm_channel_area_step(src_area); 122 dst_step = snd_pcm_channel_area_step(dst_area); 123 frames1 = frames; 124 while (frames1-- > 0) { 125 goto *get32; 126 #define GET32_END sample_loaded 127 #include "plugin_ops.h" 128 #undef GET32_END 129 sample_loaded: 130 goto *put32float; 131 #define PUT32F_END sample_put 132 #include "plugin_ops.h" 133 #undef PUT32F_END 134 sample_put: 135 src += src_step; 136 dst += dst_step; 137 } 138 } 139 } 140 141 void snd_pcm_lfloat_convert_float_integer(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, 142 const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, 143 unsigned int channels, snd_pcm_uframes_t frames, 144 unsigned int put32idx, unsigned int get32floatidx) 145 { 146 #define PUT32_LABELS 147 #define GET32F_LABELS 148 #include "plugin_ops.h" 149 #undef GET32F_LABELS 150 #undef PUT32_LABELS 151 void *put32 = put32_labels[put32idx]; 152 void *get32float = get32float_labels[get32floatidx]; 153 unsigned int channel; 154 for (channel = 0; channel < channels; ++channel) { 155 const char *src; 156 char *dst; 157 int src_step, dst_step; 158 snd_pcm_uframes_t frames1; 159 int32_t sample = 0; 160 snd_tmp_float_t tmp_float; 161 snd_tmp_double_t tmp_double; 162 const snd_pcm_channel_area_t *src_area = &src_areas[channel]; 163 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; 164 src = snd_pcm_channel_area_addr(src_area, src_offset); 165 dst = snd_pcm_channel_area_addr(dst_area, dst_offset); 166 src_step = snd_pcm_channel_area_step(src_area); 167 dst_step = snd_pcm_channel_area_step(dst_area); 168 frames1 = frames; 169 while (frames1-- > 0) { 170 goto *get32float; 171 #define GET32F_END sample_loaded 172 #include "plugin_ops.h" 173 #undef GET32F_END 174 sample_loaded: 175 goto *put32; 176 #define PUT32_END sample_put 177 #include "plugin_ops.h" 178 #undef PUT32_END 179 sample_put: 180 src += src_step; 181 dst += dst_step; 182 } 183 } 184 } 185 186 #endif /* DOC_HIDDEN */ 187 188 static int snd_pcm_lfloat_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 189 { 190 snd_pcm_lfloat_t *lfloat = pcm->private_data; 191 int err; 192 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; 193 snd_pcm_format_mask_t lformat_mask = { SND_PCM_FMTBIT_LINEAR }; 194 snd_pcm_format_mask_t fformat_mask = { SND_PCM_FMTBIT_FLOAT }; 195 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, 196 &access_mask); 197 if (err < 0) 198 return err; 199 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT, 200 snd_pcm_format_linear(lfloat->sformat) ? 201 &fformat_mask : &lformat_mask); 202 if (err < 0) 203 return err; 204 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD); 205 if (err < 0) 206 return err; 207 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); 208 return 0; 209 } 210 211 static int snd_pcm_lfloat_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams) 212 { 213 snd_pcm_lfloat_t *lfloat = pcm->private_data; 214 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; 215 _snd_pcm_hw_params_any(sparams); 216 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, 217 &saccess_mask); 218 _snd_pcm_hw_params_set_format(sparams, lfloat->sformat); 219 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD); 220 return 0; 221 } 222 223 static int snd_pcm_lfloat_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, 224 snd_pcm_hw_params_t *sparams) 225 { 226 int err; 227 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | 228 SND_PCM_HW_PARBIT_RATE | 229 SND_PCM_HW_PARBIT_PERIOD_SIZE | 230 SND_PCM_HW_PARBIT_BUFFER_SIZE | 231 SND_PCM_HW_PARBIT_PERIODS | 232 SND_PCM_HW_PARBIT_PERIOD_TIME | 233 SND_PCM_HW_PARBIT_BUFFER_TIME | 234 SND_PCM_HW_PARBIT_TICK_TIME); 235 err = _snd_pcm_hw_params_refine(sparams, links, params); 236 if (err < 0) 237 return err; 238 return 0; 239 } 240 241 static int snd_pcm_lfloat_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, 242 snd_pcm_hw_params_t *sparams) 243 { 244 int err; 245 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | 246 SND_PCM_HW_PARBIT_RATE | 247 SND_PCM_HW_PARBIT_PERIOD_SIZE | 248 SND_PCM_HW_PARBIT_BUFFER_SIZE | 249 SND_PCM_HW_PARBIT_PERIODS | 250 SND_PCM_HW_PARBIT_PERIOD_TIME | 251 SND_PCM_HW_PARBIT_BUFFER_TIME | 252 SND_PCM_HW_PARBIT_TICK_TIME); 253 err = _snd_pcm_hw_params_refine(params, links, sparams); 254 if (err < 0) 255 return err; 256 return 0; 257 } 258 259 static int snd_pcm_lfloat_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 260 { 261 return snd_pcm_hw_refine_slave(pcm, params, 262 snd_pcm_lfloat_hw_refine_cprepare, 263 snd_pcm_lfloat_hw_refine_cchange, 264 snd_pcm_lfloat_hw_refine_sprepare, 265 snd_pcm_lfloat_hw_refine_schange, 266 snd_pcm_generic_hw_refine); 267 } 268 269 static int snd_pcm_lfloat_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 270 { 271 snd_pcm_lfloat_t *lfloat = pcm->private_data; 272 snd_pcm_t *slave = lfloat->plug.gen.slave; 273 snd_pcm_format_t src_format, dst_format; 274 int err = snd_pcm_hw_params_slave(pcm, params, 275 snd_pcm_lfloat_hw_refine_cchange, 276 snd_pcm_lfloat_hw_refine_sprepare, 277 snd_pcm_lfloat_hw_refine_schange, 278 snd_pcm_generic_hw_params); 279 if (err < 0) 280 return err; 281 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 282 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format); 283 dst_format = slave->format; 284 } else { 285 src_format = slave->format; 286 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format); 287 } 288 if (snd_pcm_format_linear(src_format)) { 289 lfloat->int32_idx = snd_pcm_linear_get32_index(src_format, SND_PCM_FORMAT_S32); 290 lfloat->float32_idx = snd_pcm_lfloat_put_s32_index(dst_format); 291 lfloat->func = snd_pcm_lfloat_convert_integer_float; 292 } else { 293 lfloat->int32_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, dst_format); 294 lfloat->float32_idx = snd_pcm_lfloat_get_s32_index(src_format); 295 lfloat->func = snd_pcm_lfloat_convert_float_integer; 296 } 297 return 0; 298 } 299 300 static snd_pcm_uframes_t 301 snd_pcm_lfloat_write_areas(snd_pcm_t *pcm, 302 const snd_pcm_channel_area_t *areas, 303 snd_pcm_uframes_t offset, 304 snd_pcm_uframes_t size, 305 const snd_pcm_channel_area_t *slave_areas, 306 snd_pcm_uframes_t slave_offset, 307 snd_pcm_uframes_t *slave_sizep) 308 { 309 snd_pcm_lfloat_t *lfloat = pcm->private_data; 310 if (size > *slave_sizep) 311 size = *slave_sizep; 312 lfloat->func(slave_areas, slave_offset, 313 areas, offset, 314 pcm->channels, size, 315 lfloat->int32_idx, lfloat->float32_idx); 316 *slave_sizep = size; 317 return size; 318 } 319 320 static snd_pcm_uframes_t 321 snd_pcm_lfloat_read_areas(snd_pcm_t *pcm, 322 const snd_pcm_channel_area_t *areas, 323 snd_pcm_uframes_t offset, 324 snd_pcm_uframes_t size, 325 const snd_pcm_channel_area_t *slave_areas, 326 snd_pcm_uframes_t slave_offset, 327 snd_pcm_uframes_t *slave_sizep) 328 { 329 snd_pcm_lfloat_t *lfloat = pcm->private_data; 330 if (size > *slave_sizep) 331 size = *slave_sizep; 332 lfloat->func(areas, offset, 333 slave_areas, slave_offset, 334 pcm->channels, size, 335 lfloat->int32_idx, lfloat->float32_idx); 336 *slave_sizep = size; 337 return size; 338 } 339 340 static void snd_pcm_lfloat_dump(snd_pcm_t *pcm, snd_output_t *out) 341 { 342 snd_pcm_lfloat_t *lfloat = pcm->private_data; 343 snd_output_printf(out, "Linear Integer <-> Linear Float conversion PCM (%s)\n", 344 snd_pcm_format_name(lfloat->sformat)); 345 if (pcm->setup) { 346 snd_output_printf(out, "Its setup is:\n"); 347 snd_pcm_dump_setup(pcm, out); 348 } 349 snd_output_printf(out, "Slave: "); 350 snd_pcm_dump(lfloat->plug.gen.slave, out); 351 } 352 353 static const snd_pcm_ops_t snd_pcm_lfloat_ops = { 354 .close = snd_pcm_generic_close, 355 .info = snd_pcm_generic_info, 356 .hw_refine = snd_pcm_lfloat_hw_refine, 357 .hw_params = snd_pcm_lfloat_hw_params, 358 .hw_free = snd_pcm_generic_hw_free, 359 .sw_params = snd_pcm_generic_sw_params, 360 .channel_info = snd_pcm_generic_channel_info, 361 .dump = snd_pcm_lfloat_dump, 362 .nonblock = snd_pcm_generic_nonblock, 363 .async = snd_pcm_generic_async, 364 .mmap = snd_pcm_generic_mmap, 365 .munmap = snd_pcm_generic_munmap, 366 }; 367 368 /** 369 * \brief Creates a new linear conversion PCM 370 * \param pcmp Returns created PCM handle 371 * \param name Name of PCM 372 * \param sformat Slave (destination) format 373 * \param slave Slave PCM handle 374 * \param close_slave When set, the slave PCM handle is closed with copy PCM 375 * \retval zero on success otherwise a negative error code 376 * \warning Using of this function might be dangerous in the sense 377 * of compatibility reasons. The prototype might be freely 378 * changed in future. 379 */ 380 int snd_pcm_lfloat_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave) 381 { 382 snd_pcm_t *pcm; 383 snd_pcm_lfloat_t *lfloat; 384 int err; 385 assert(pcmp && slave); 386 if (snd_pcm_format_linear(sformat) != 1 && 387 snd_pcm_format_float(sformat) != 1) 388 return -EINVAL; 389 lfloat = calloc(1, sizeof(snd_pcm_lfloat_t)); 390 if (!lfloat) { 391 return -ENOMEM; 392 } 393 snd_pcm_plugin_init(&lfloat->plug); 394 lfloat->sformat = sformat; 395 lfloat->plug.read = snd_pcm_lfloat_read_areas; 396 lfloat->plug.write = snd_pcm_lfloat_write_areas; 397 lfloat->plug.undo_read = snd_pcm_plugin_undo_read_generic; 398 lfloat->plug.undo_write = snd_pcm_plugin_undo_write_generic; 399 lfloat->plug.gen.slave = slave; 400 lfloat->plug.gen.close_slave = close_slave; 401 402 err = snd_pcm_new(&pcm, SND_PCM_TYPE_LINEAR_FLOAT, name, slave->stream, slave->mode); 403 if (err < 0) { 404 free(lfloat); 405 return err; 406 } 407 pcm->ops = &snd_pcm_lfloat_ops; 408 pcm->fast_ops = &snd_pcm_plugin_fast_ops; 409 pcm->private_data = lfloat; 410 pcm->poll_fd = slave->poll_fd; 411 pcm->poll_events = slave->poll_events; 412 pcm->monotonic = slave->monotonic; 413 snd_pcm_set_hw_ptr(pcm, &lfloat->plug.hw_ptr, -1, 0); 414 snd_pcm_set_appl_ptr(pcm, &lfloat->plug.appl_ptr, -1, 0); 415 *pcmp = pcm; 416 417 return 0; 418 } 419 420 /*! \page pcm_plugins 421 422 \section pcm_plugins_lfloat Plugin: linear<->float 423 424 This plugin converts linear to float samples and float to linear samples from master 425 linear<->float conversion PCM to given slave PCM. The channel count, format and rate must 426 match for both of them. 427 428 \code 429 pcm.name { 430 type lfloat # Linear<->Float conversion PCM 431 slave STR # Slave name 432 # or 433 slave { # Slave definition 434 pcm STR # Slave PCM name 435 # or 436 pcm { } # Slave PCM definition 437 format STR # Slave format 438 } 439 } 440 \endcode 441 442 \subsection pcm_plugins_lfloat_funcref Function reference 443 444 <UL> 445 <LI>snd_pcm_lfloat_open() 446 <LI>_snd_pcm_lfloat_open() 447 </UL> 448 449 */ 450 451 /** 452 * \brief Creates a new linear<->float conversion PCM 453 * \param pcmp Returns created PCM handle 454 * \param name Name of PCM 455 * \param root Root configuration node 456 * \param conf Configuration node with copy PCM description 457 * \param stream Stream type 458 * \param mode Stream mode 459 * \retval zero on success otherwise a negative error code 460 * \warning Using of this function might be dangerous in the sense 461 * of compatibility reasons. The prototype might be freely 462 * changed in future. 463 */ 464 int _snd_pcm_lfloat_open(snd_pcm_t **pcmp, const char *name, 465 snd_config_t *root, snd_config_t *conf, 466 snd_pcm_stream_t stream, int mode) 467 { 468 snd_config_iterator_t i, next; 469 int err; 470 snd_pcm_t *spcm; 471 snd_config_t *slave = NULL, *sconf; 472 snd_pcm_format_t sformat; 473 snd_config_for_each(i, next, conf) { 474 snd_config_t *n = snd_config_iterator_entry(i); 475 const char *id; 476 if (snd_config_get_id(n, &id) < 0) 477 continue; 478 if (snd_pcm_conf_generic_id(id)) 479 continue; 480 if (strcmp(id, "slave") == 0) { 481 slave = n; 482 continue; 483 } 484 SNDERR("Unknown field %s", id); 485 return -EINVAL; 486 } 487 if (!slave) { 488 SNDERR("slave is not defined"); 489 return -EINVAL; 490 } 491 err = snd_pcm_slave_conf(root, slave, &sconf, 1, 492 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat); 493 if (err < 0) 494 return err; 495 if (snd_pcm_format_linear(sformat) != 1 && 496 snd_pcm_format_float(sformat) != 1) { 497 snd_config_delete(sconf); 498 SNDERR("slave format is not linear integer or linear float"); 499 return -EINVAL; 500 } 501 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); 502 snd_config_delete(sconf); 503 if (err < 0) 504 return err; 505 err = snd_pcm_lfloat_open(pcmp, name, sformat, spcm, 1); 506 if (err < 0) 507 snd_pcm_close(spcm); 508 return err; 509 } 510 #ifndef DOC_HIDDEN 511 SND_DLSYM_BUILD_VERSION(_snd_pcm_lfloat_open, SND_PCM_DLSYM_VERSION); 512 #endif 513 514 #else /* BUGGY_GCC */ 515 516 int snd_pcm_lfloat_open(snd_pcm_t **pcmp ATTRIBUTE_UNUSED, 517 const char *name ATTRIBUTE_UNUSED, 518 snd_pcm_format_t sformat ATTRIBUTE_UNUSED, 519 snd_pcm_t *slave ATTRIBUTE_UNUSED, 520 int close_slave ATTRIBUTE_UNUSED) 521 { 522 SNDERR("please, upgrade your GCC to use lfloat plugin"); 523 return -EINVAL; 524 } 525 526 int _snd_pcm_lfloat_open(snd_pcm_t **pcmp ATTRIBUTE_UNUSED, 527 const char *name ATTRIBUTE_UNUSED, 528 snd_config_t *root ATTRIBUTE_UNUSED, 529 snd_config_t *conf ATTRIBUTE_UNUSED, 530 snd_pcm_stream_t stream ATTRIBUTE_UNUSED, 531 int mode ATTRIBUTE_UNUSED) 532 { 533 SNDERR("please, upgrade your GCC to use lfloat plugin"); 534 return -EINVAL; 535 } 536 537 #endif /* BUGGY_GCC */ 538