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