1 /** 2 * \file pcm/pcm_meter.c 3 * \brief Helper functions for #SND_PCM_TYPE_METER PCM scopes 4 * \author Abramo Bagnara <abramo (at) alsa-project.org> 5 * \date 2001 6 * 7 * Helper functions for #SND_PCM_TYPE_METER PCM scopes 8 */ 9 /* 10 * PCM - Meter plugin 11 * Copyright (c) 2001 by Abramo Bagnara <abramo (at) alsa-project.org> 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 30 #include <byteswap.h> 31 #include <time.h> 32 #include <pthread.h> 33 #include <dlfcn.h> 34 #include "pcm_local.h" 35 #include "pcm_plugin.h" 36 #include "iatomic.h" 37 38 #ifndef PIC 39 /* entry for static linking */ 40 const char *_snd_module_pcm_meter = ""; 41 #endif 42 43 #ifndef DOC_HIDDEN 44 #define FREQUENCY 50 45 46 struct _snd_pcm_scope { 47 int enabled; 48 char *name; 49 snd_pcm_scope_ops_t *ops; 50 void *private_data; 51 struct list_head list; 52 }; 53 54 typedef struct _snd_pcm_meter { 55 snd_pcm_generic_t gen; 56 snd_pcm_uframes_t rptr; 57 snd_pcm_uframes_t buf_size; 58 snd_pcm_channel_area_t *buf_areas; 59 snd_pcm_uframes_t now; 60 unsigned char *buf; 61 struct list_head scopes; 62 int closed; 63 int running; 64 atomic_t reset; 65 pthread_t thread; 66 pthread_mutex_t update_mutex; 67 pthread_mutex_t running_mutex; 68 pthread_cond_t running_cond; 69 struct timespec delay; 70 void *dl_handle; 71 } snd_pcm_meter_t; 72 73 static void snd_pcm_meter_add_frames(snd_pcm_t *pcm, 74 const snd_pcm_channel_area_t *areas, 75 snd_pcm_uframes_t ptr, 76 snd_pcm_uframes_t frames) 77 { 78 snd_pcm_meter_t *meter = pcm->private_data; 79 while (frames > 0) { 80 snd_pcm_uframes_t n = frames; 81 snd_pcm_uframes_t dst_offset = ptr % meter->buf_size; 82 snd_pcm_uframes_t src_offset = ptr % pcm->buffer_size; 83 snd_pcm_uframes_t dst_cont = meter->buf_size - dst_offset; 84 snd_pcm_uframes_t src_cont = pcm->buffer_size - src_offset; 85 if (n > dst_cont) 86 n = dst_cont; 87 if (n > src_cont) 88 n = src_cont; 89 snd_pcm_areas_copy(meter->buf_areas, dst_offset, 90 areas, src_offset, 91 pcm->channels, n, pcm->format); 92 frames -= n; 93 ptr += n; 94 if (ptr == pcm->boundary) 95 ptr = 0; 96 } 97 } 98 99 static void snd_pcm_meter_update_main(snd_pcm_t *pcm) 100 { 101 snd_pcm_meter_t *meter = pcm->private_data; 102 snd_pcm_sframes_t frames; 103 snd_pcm_uframes_t rptr, old_rptr; 104 const snd_pcm_channel_area_t *areas; 105 int locked; 106 locked = (pthread_mutex_trylock(&meter->update_mutex) >= 0); 107 areas = snd_pcm_mmap_areas(pcm); 108 rptr = *pcm->hw.ptr; 109 old_rptr = meter->rptr; 110 meter->rptr = rptr; 111 frames = rptr - old_rptr; 112 if (frames < 0) 113 frames += pcm->boundary; 114 if (frames > 0) { 115 assert((snd_pcm_uframes_t) frames <= pcm->buffer_size); 116 snd_pcm_meter_add_frames(pcm, areas, old_rptr, 117 (snd_pcm_uframes_t) frames); 118 } 119 if (locked) 120 pthread_mutex_unlock(&meter->update_mutex); 121 } 122 123 static int snd_pcm_meter_update_scope(snd_pcm_t *pcm) 124 { 125 snd_pcm_meter_t *meter = pcm->private_data; 126 snd_pcm_sframes_t frames; 127 snd_pcm_uframes_t rptr, old_rptr; 128 const snd_pcm_channel_area_t *areas; 129 int reset = 0; 130 /* Wait main thread */ 131 pthread_mutex_lock(&meter->update_mutex); 132 areas = snd_pcm_mmap_areas(pcm); 133 _again: 134 rptr = *pcm->hw.ptr; 135 old_rptr = meter->rptr; 136 rmb(); 137 if (atomic_read(&meter->reset)) { 138 reset = 1; 139 atomic_dec(&meter->reset); 140 goto _again; 141 } 142 meter->rptr = rptr; 143 frames = rptr - old_rptr; 144 if (frames < 0) 145 frames += pcm->boundary; 146 if (frames > 0) { 147 assert((snd_pcm_uframes_t) frames <= pcm->buffer_size); 148 snd_pcm_meter_add_frames(pcm, areas, old_rptr, 149 (snd_pcm_uframes_t) frames); 150 } 151 pthread_mutex_unlock(&meter->update_mutex); 152 return reset; 153 } 154 155 static int snd_pcm_scope_remove(snd_pcm_scope_t *scope) 156 { 157 free(scope->name); 158 scope->ops->close(scope); 159 list_del(&scope->list); 160 free(scope); 161 return 0; 162 } 163 164 static int snd_pcm_scope_enable(snd_pcm_scope_t *scope) 165 { 166 int err; 167 assert(!scope->enabled); 168 err = scope->ops->enable(scope); 169 scope->enabled = (err >= 0); 170 return err; 171 } 172 173 static int snd_pcm_scope_disable(snd_pcm_scope_t *scope) 174 { 175 assert(scope->enabled); 176 scope->ops->disable(scope); 177 scope->enabled = 0; 178 return 0; 179 } 180 181 static void *snd_pcm_meter_thread(void *data) 182 { 183 snd_pcm_t *pcm = data; 184 snd_pcm_meter_t *meter = pcm->private_data; 185 snd_pcm_t *spcm = meter->gen.slave; 186 struct list_head *pos; 187 snd_pcm_scope_t *scope; 188 int reset; 189 list_for_each(pos, &meter->scopes) { 190 scope = list_entry(pos, snd_pcm_scope_t, list); 191 snd_pcm_scope_enable(scope); 192 } 193 while (!meter->closed) { 194 snd_pcm_sframes_t now; 195 snd_pcm_status_t status; 196 int err; 197 pthread_mutex_lock(&meter->running_mutex); 198 err = snd_pcm_status(spcm, &status); 199 assert(err >= 0); 200 if (status.state != SND_PCM_STATE_RUNNING && 201 (status.state != SND_PCM_STATE_DRAINING || 202 spcm->stream != SND_PCM_STREAM_PLAYBACK)) { 203 if (meter->running) { 204 list_for_each(pos, &meter->scopes) { 205 scope = list_entry(pos, snd_pcm_scope_t, list); 206 scope->ops->stop(scope); 207 } 208 meter->running = 0; 209 } 210 pthread_cond_wait(&meter->running_cond, 211 &meter->running_mutex); 212 pthread_mutex_unlock(&meter->running_mutex); 213 continue; 214 } 215 pthread_mutex_unlock(&meter->running_mutex); 216 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 217 now = status.appl_ptr - status.delay; 218 if (now < 0) 219 now += pcm->boundary; 220 } else { 221 now = status.appl_ptr + status.delay; 222 if ((snd_pcm_uframes_t) now >= pcm->boundary) 223 now -= pcm->boundary; 224 } 225 meter->now = now; 226 if (pcm->stream == SND_PCM_STREAM_CAPTURE) 227 reset = snd_pcm_meter_update_scope(pcm); 228 else { 229 reset = 0; 230 while (atomic_read(&meter->reset)) { 231 reset = 1; 232 atomic_dec(&meter->reset); 233 } 234 } 235 if (reset) { 236 list_for_each(pos, &meter->scopes) { 237 scope = list_entry(pos, snd_pcm_scope_t, list); 238 if (scope->enabled) 239 scope->ops->reset(scope); 240 } 241 continue; 242 } 243 if (!meter->running) { 244 list_for_each(pos, &meter->scopes) { 245 scope = list_entry(pos, snd_pcm_scope_t, list); 246 if (scope->enabled) 247 scope->ops->start(scope); 248 } 249 meter->running = 1; 250 } 251 list_for_each(pos, &meter->scopes) { 252 scope = list_entry(pos, snd_pcm_scope_t, list); 253 if (scope->enabled) 254 scope->ops->update(scope); 255 } 256 nanosleep(&meter->delay, NULL); 257 } 258 list_for_each(pos, &meter->scopes) { 259 scope = list_entry(pos, snd_pcm_scope_t, list); 260 if (scope->enabled) 261 snd_pcm_scope_disable(scope); 262 } 263 return NULL; 264 } 265 266 static int snd_pcm_meter_close(snd_pcm_t *pcm) 267 { 268 snd_pcm_meter_t *meter = pcm->private_data; 269 struct list_head *pos, *npos; 270 int err = 0; 271 pthread_mutex_destroy(&meter->update_mutex); 272 pthread_mutex_destroy(&meter->running_mutex); 273 pthread_cond_destroy(&meter->running_cond); 274 if (meter->gen.close_slave) 275 err = snd_pcm_close(meter->gen.slave); 276 list_for_each_safe(pos, npos, &meter->scopes) { 277 snd_pcm_scope_t *scope; 278 scope = list_entry(pos, snd_pcm_scope_t, list); 279 snd_pcm_scope_remove(scope); 280 } 281 if (meter->dl_handle) 282 snd_dlclose(meter->dl_handle); 283 free(meter); 284 return err; 285 } 286 287 static int snd_pcm_meter_prepare(snd_pcm_t *pcm) 288 { 289 snd_pcm_meter_t *meter = pcm->private_data; 290 int err; 291 atomic_inc(&meter->reset); 292 err = snd_pcm_prepare(meter->gen.slave); 293 if (err >= 0) { 294 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) 295 meter->rptr = *pcm->appl.ptr; 296 else 297 meter->rptr = *pcm->hw.ptr; 298 } 299 return err; 300 } 301 302 static int snd_pcm_meter_reset(snd_pcm_t *pcm) 303 { 304 snd_pcm_meter_t *meter = pcm->private_data; 305 int err = snd_pcm_reset(meter->gen.slave); 306 if (err >= 0) { 307 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) 308 meter->rptr = *pcm->appl.ptr; 309 } 310 return err; 311 } 312 313 static int snd_pcm_meter_start(snd_pcm_t *pcm) 314 { 315 snd_pcm_meter_t *meter = pcm->private_data; 316 int err; 317 pthread_mutex_lock(&meter->running_mutex); 318 err = snd_pcm_start(meter->gen.slave); 319 if (err >= 0) 320 pthread_cond_signal(&meter->running_cond); 321 pthread_mutex_unlock(&meter->running_mutex); 322 return err; 323 } 324 325 static snd_pcm_sframes_t snd_pcm_meter_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 326 { 327 snd_pcm_meter_t *meter = pcm->private_data; 328 snd_pcm_sframes_t err = snd_pcm_rewind(meter->gen.slave, frames); 329 if (err > 0 && pcm->stream == SND_PCM_STREAM_PLAYBACK) 330 meter->rptr = *pcm->appl.ptr; 331 return err; 332 } 333 334 static snd_pcm_sframes_t snd_pcm_meter_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 335 { 336 snd_pcm_meter_t *meter = pcm->private_data; 337 snd_pcm_sframes_t err = INTERNAL(snd_pcm_forward)(meter->gen.slave, frames); 338 if (err > 0 && pcm->stream == SND_PCM_STREAM_PLAYBACK) 339 meter->rptr = *pcm->appl.ptr; 340 return err; 341 } 342 343 static snd_pcm_sframes_t snd_pcm_meter_mmap_commit(snd_pcm_t *pcm, 344 snd_pcm_uframes_t offset, 345 snd_pcm_uframes_t size) 346 { 347 snd_pcm_meter_t *meter = pcm->private_data; 348 snd_pcm_uframes_t old_rptr = *pcm->appl.ptr; 349 snd_pcm_sframes_t result = snd_pcm_mmap_commit(meter->gen.slave, offset, size); 350 if (result <= 0) 351 return result; 352 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 353 snd_pcm_meter_add_frames(pcm, snd_pcm_mmap_areas(pcm), old_rptr, result); 354 meter->rptr = *pcm->appl.ptr; 355 } 356 return result; 357 } 358 359 static snd_pcm_sframes_t snd_pcm_meter_avail_update(snd_pcm_t *pcm) 360 { 361 snd_pcm_meter_t *meter = pcm->private_data; 362 snd_pcm_sframes_t result = snd_pcm_avail_update(meter->gen.slave); 363 if (result <= 0) 364 return result; 365 if (pcm->stream == SND_PCM_STREAM_CAPTURE) 366 snd_pcm_meter_update_main(pcm); 367 return result; 368 } 369 370 static int snd_pcm_meter_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) 371 { 372 int err; 373 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; 374 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, 375 &access_mask); 376 if (err < 0) 377 return err; 378 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); 379 return 0; 380 } 381 382 static int snd_pcm_meter_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams) 383 { 384 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; 385 _snd_pcm_hw_params_any(sparams); 386 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, 387 &saccess_mask); 388 return 0; 389 } 390 391 static int snd_pcm_meter_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, 392 snd_pcm_hw_params_t *sparams) 393 { 394 int err; 395 unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS; 396 err = _snd_pcm_hw_params_refine(sparams, links, params); 397 if (err < 0) 398 return err; 399 return 0; 400 } 401 402 static int snd_pcm_meter_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, 403 snd_pcm_hw_params_t *sparams) 404 { 405 int err; 406 unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS; 407 err = _snd_pcm_hw_params_refine(params, links, sparams); 408 if (err < 0) 409 return err; 410 return 0; 411 } 412 413 static int snd_pcm_meter_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 414 { 415 snd_pcm_meter_t *meter = pcm->private_data; 416 return snd_pcm_hw_refine(meter->gen.slave, params); 417 } 418 419 static int snd_pcm_meter_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 420 { 421 snd_pcm_meter_t *meter = pcm->private_data; 422 return _snd_pcm_hw_params(meter->gen.slave, params); 423 } 424 425 static int snd_pcm_meter_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 426 { 427 return snd_pcm_hw_refine_slave(pcm, params, 428 snd_pcm_meter_hw_refine_cprepare, 429 snd_pcm_meter_hw_refine_cchange, 430 snd_pcm_meter_hw_refine_sprepare, 431 snd_pcm_meter_hw_refine_schange, 432 snd_pcm_meter_hw_refine_slave); 433 } 434 435 static int snd_pcm_meter_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) 436 { 437 snd_pcm_meter_t *meter = pcm->private_data; 438 unsigned int channel; 439 snd_pcm_t *slave = meter->gen.slave; 440 size_t buf_size_bytes; 441 int err; 442 err = snd_pcm_hw_params_slave(pcm, params, 443 snd_pcm_meter_hw_refine_cchange, 444 snd_pcm_meter_hw_refine_sprepare, 445 snd_pcm_meter_hw_refine_schange, 446 snd_pcm_meter_hw_params_slave); 447 if (err < 0) 448 return err; 449 /* more than 1 second of buffer */ 450 meter->buf_size = slave->buffer_size; 451 while (meter->buf_size < slave->rate) 452 meter->buf_size *= 2; 453 buf_size_bytes = snd_pcm_frames_to_bytes(slave, meter->buf_size); 454 assert(!meter->buf); 455 meter->buf = malloc(buf_size_bytes); 456 if (!meter->buf) 457 return -ENOMEM; 458 meter->buf_areas = malloc(sizeof(*meter->buf_areas) * slave->channels); 459 if (!meter->buf_areas) { 460 free(meter->buf); 461 return -ENOMEM; 462 } 463 for (channel = 0; channel < slave->channels; ++channel) { 464 snd_pcm_channel_area_t *a = &meter->buf_areas[channel]; 465 a->addr = meter->buf + buf_size_bytes / slave->channels * channel; 466 a->first = 0; 467 a->step = slave->sample_bits; 468 } 469 meter->closed = 0; 470 err = pthread_create(&meter->thread, NULL, snd_pcm_meter_thread, pcm); 471 assert(err == 0); 472 return 0; 473 } 474 475 static int snd_pcm_meter_hw_free(snd_pcm_t *pcm) 476 { 477 snd_pcm_meter_t *meter = pcm->private_data; 478 int err; 479 meter->closed = 1; 480 pthread_mutex_lock(&meter->running_mutex); 481 pthread_cond_signal(&meter->running_cond); 482 pthread_mutex_unlock(&meter->running_mutex); 483 err = pthread_join(meter->thread, 0); 484 assert(err == 0); 485 free(meter->buf); 486 free(meter->buf_areas); 487 meter->buf = NULL; 488 meter->buf_areas = NULL; 489 return snd_pcm_hw_free(meter->gen.slave); 490 } 491 492 static void snd_pcm_meter_dump(snd_pcm_t *pcm, snd_output_t *out) 493 { 494 snd_pcm_meter_t *meter = pcm->private_data; 495 snd_output_printf(out, "Meter PCM\n"); 496 if (pcm->setup) { 497 snd_output_printf(out, "Its setup is:\n"); 498 snd_pcm_dump_setup(pcm, out); 499 } 500 snd_output_printf(out, "Slave: "); 501 snd_pcm_dump(meter->gen.slave, out); 502 } 503 504 static const snd_pcm_ops_t snd_pcm_meter_ops = { 505 .close = snd_pcm_meter_close, 506 .info = snd_pcm_generic_info, 507 .hw_refine = snd_pcm_meter_hw_refine, 508 .hw_params = snd_pcm_meter_hw_params, 509 .hw_free = snd_pcm_meter_hw_free, 510 .sw_params = snd_pcm_generic_sw_params, 511 .channel_info = snd_pcm_generic_channel_info, 512 .dump = snd_pcm_meter_dump, 513 .nonblock = snd_pcm_generic_nonblock, 514 .async = snd_pcm_generic_async, 515 .mmap = snd_pcm_generic_mmap, 516 .munmap = snd_pcm_generic_munmap, 517 }; 518 519 static const snd_pcm_fast_ops_t snd_pcm_meter_fast_ops = { 520 .status = snd_pcm_generic_status, 521 .state = snd_pcm_generic_state, 522 .hwsync = snd_pcm_generic_hwsync, 523 .delay = snd_pcm_generic_delay, 524 .prepare = snd_pcm_meter_prepare, 525 .reset = snd_pcm_meter_reset, 526 .start = snd_pcm_meter_start, 527 .drop = snd_pcm_generic_drop, 528 .drain = snd_pcm_generic_drain, 529 .pause = snd_pcm_generic_pause, 530 .rewindable = snd_pcm_generic_rewindable, 531 .rewind = snd_pcm_meter_rewind, 532 .forwardable = snd_pcm_generic_forwardable, 533 .forward = snd_pcm_meter_forward, 534 .resume = snd_pcm_generic_resume, 535 .writei = snd_pcm_mmap_writei, 536 .writen = snd_pcm_mmap_writen, 537 .readi = snd_pcm_mmap_readi, 538 .readn = snd_pcm_mmap_readn, 539 .avail_update = snd_pcm_meter_avail_update, 540 .mmap_commit = snd_pcm_meter_mmap_commit, 541 .htimestamp = snd_pcm_generic_htimestamp, 542 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, 543 .poll_descriptors = snd_pcm_generic_poll_descriptors, 544 .poll_revents = snd_pcm_generic_poll_revents, 545 }; 546 547 /** 548 * \brief Creates a new Meter PCM 549 * \param pcmp Returns created PCM handle 550 * \param name Name of PCM 551 * \param frequency Update frequency 552 * \param slave Slave PCM handle 553 * \param close_slave When set, the slave PCM handle is closed with copy PCM 554 * \retval zero on success otherwise a negative error code 555 * \warning Using of this function might be dangerous in the sense 556 * of compatibility reasons. The prototype might be freely 557 * changed in future. 558 */ 559 int snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, unsigned int frequency, 560 snd_pcm_t *slave, int close_slave) 561 { 562 snd_pcm_t *pcm; 563 snd_pcm_meter_t *meter; 564 int err; 565 assert(pcmp); 566 meter = calloc(1, sizeof(snd_pcm_meter_t)); 567 if (!meter) 568 return -ENOMEM; 569 meter->gen.slave = slave; 570 meter->gen.close_slave = close_slave; 571 meter->delay.tv_sec = 0; 572 meter->delay.tv_nsec = 1000000000 / frequency; 573 INIT_LIST_HEAD(&meter->scopes); 574 575 err = snd_pcm_new(&pcm, SND_PCM_TYPE_METER, name, slave->stream, slave->mode); 576 if (err < 0) { 577 free(meter); 578 return err; 579 } 580 pcm->mmap_rw = 1; 581 pcm->mmap_shadow = 1; 582 pcm->ops = &snd_pcm_meter_ops; 583 pcm->fast_ops = &snd_pcm_meter_fast_ops; 584 pcm->private_data = meter; 585 pcm->poll_fd = slave->poll_fd; 586 pcm->poll_events = slave->poll_events; 587 pcm->monotonic = slave->monotonic; 588 snd_pcm_link_hw_ptr(pcm, slave); 589 snd_pcm_link_appl_ptr(pcm, slave); 590 *pcmp = pcm; 591 592 pthread_mutex_init(&meter->update_mutex, NULL); 593 pthread_mutex_init(&meter->running_mutex, NULL); 594 pthread_cond_init(&meter->running_cond, NULL); 595 return 0; 596 } 597 598 599 static int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name, 600 snd_config_t *root, snd_config_t *conf) 601 { 602 char buf[256]; 603 snd_config_iterator_t i, next; 604 const char *id; 605 const char *lib = NULL, *open_name = NULL, *str = NULL; 606 snd_config_t *c, *type_conf = NULL; 607 int (*open_func)(snd_pcm_t *, const char *, 608 snd_config_t *, snd_config_t *) = NULL; 609 snd_pcm_meter_t *meter = pcm->private_data; 610 void *h = NULL; 611 int err; 612 613 if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) { 614 SNDERR("Invalid type for scope %s", str); 615 err = -EINVAL; 616 goto _err; 617 } 618 err = snd_config_search(conf, "type", &c); 619 if (err < 0) { 620 SNDERR("type is not defined"); 621 goto _err; 622 } 623 err = snd_config_get_id(c, &id); 624 if (err < 0) { 625 SNDERR("unable to get id"); 626 goto _err; 627 } 628 err = snd_config_get_string(c, &str); 629 if (err < 0) { 630 SNDERR("Invalid type for %s", id); 631 goto _err; 632 } 633 err = snd_config_search_definition(root, "pcm_scope_type", str, &type_conf); 634 if (err >= 0) { 635 snd_config_for_each(i, next, type_conf) { 636 snd_config_t *n = snd_config_iterator_entry(i); 637 const char *id; 638 if (snd_config_get_id(n, &id) < 0) 639 continue; 640 if (strcmp(id, "comment") == 0) 641 continue; 642 if (strcmp(id, "lib") == 0) { 643 err = snd_config_get_string(n, &lib); 644 if (err < 0) { 645 SNDERR("Invalid type for %s", id); 646 goto _err; 647 } 648 continue; 649 } 650 if (strcmp(id, "open") == 0) { 651 err = snd_config_get_string(n, &open_name); 652 if (err < 0) { 653 SNDERR("Invalid type for %s", id); 654 goto _err; 655 } 656 continue; 657 } 658 SNDERR("Unknown field %s", id); 659 err = -EINVAL; 660 goto _err; 661 } 662 } 663 if (!open_name) { 664 open_name = buf; 665 snprintf(buf, sizeof(buf), "_snd_pcm_scope_%s_open", str); 666 } 667 h = snd_dlopen(lib, RTLD_NOW); 668 open_func = h ? dlsym(h, open_name) : NULL; 669 err = 0; 670 if (!h) { 671 SNDERR("Cannot open shared library %s", lib); 672 err = -ENOENT; 673 } else if (!open_func) { 674 SNDERR("symbol %s is not defined inside %s", open_name, lib); 675 snd_dlclose(h); 676 err = -ENXIO; 677 } 678 _err: 679 if (type_conf) 680 snd_config_delete(type_conf); 681 if (! err) { 682 err = open_func(pcm, name, root, conf); 683 if (err < 0) 684 snd_dlclose(h); 685 else 686 meter->dl_handle = h; 687 } 688 return err; 689 } 690 691 /*! \page pcm_plugins 692 693 \section pcm_plugins_meter Plugin: Meter 694 695 Show meter (visual waveform representation). 696 697 \code 698 pcm_scope_type.NAME { 699 [lib STR] # Library file (default libasound.so) 700 [open STR] # Open function (default _snd_pcm_scope_NAME_open) 701 } 702 703 pcm_scope.name { 704 type STR # Scope type 705 ... 706 } 707 708 pcm.name { 709 type meter # Meter PCM 710 slave STR # Slave name 711 # or 712 slave { # Slave definition 713 pcm STR # Slave PCM name 714 # or 715 pcm { } # Slave PCM definition 716 } 717 [frequency INT] # Updates per second 718 scopes { 719 ID STR # Scope name (see pcm_scope) 720 # or 721 ID { } # Scope definition (see pcm_scope) 722 } 723 } 724 \endcode 725 726 \subsection pcm_plugins_meter_funcref Function reference 727 728 <UL> 729 <LI>snd_pcm_meter_open() 730 <LI>_snd_pcm_meter_open() 731 </UL> 732 733 */ 734 735 /** 736 * \brief Creates a new Meter PCM 737 * \param pcmp Returns created PCM handle 738 * \param name Name of PCM 739 * \param root Root configuration node 740 * \param conf Configuration node with Meter PCM description 741 * \param stream Stream type 742 * \param mode Stream mode 743 * \retval zero on success otherwise a negative error code 744 * \warning Using of this function might be dangerous in the sense 745 * of compatibility reasons. The prototype might be freely 746 * changed in future. 747 */ 748 int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, 749 snd_config_t *root, snd_config_t *conf, 750 snd_pcm_stream_t stream, int mode) 751 { 752 snd_config_iterator_t i, next; 753 int err; 754 snd_pcm_t *spcm; 755 snd_config_t *slave = NULL, *sconf; 756 long frequency = -1; 757 snd_config_t *scopes = NULL; 758 snd_config_for_each(i, next, conf) { 759 snd_config_t *n = snd_config_iterator_entry(i); 760 const char *id; 761 if (snd_config_get_id(n, &id) < 0) 762 continue; 763 if (snd_pcm_conf_generic_id(id)) 764 continue; 765 if (strcmp(id, "slave") == 0) { 766 slave = n; 767 continue; 768 } 769 if (strcmp(id, "frequency") == 0) { 770 err = snd_config_get_integer(n, &frequency); 771 if (err < 0) { 772 SNDERR("Invalid type for %s", id); 773 return -EINVAL; 774 } 775 continue; 776 } 777 if (strcmp(id, "scopes") == 0) { 778 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { 779 SNDERR("Invalid type for %s", id); 780 return -EINVAL; 781 } 782 scopes = n; 783 continue; 784 } 785 SNDERR("Unknown field %s", id); 786 return -EINVAL; 787 } 788 if (!slave) { 789 SNDERR("slave is not defined"); 790 return -EINVAL; 791 } 792 err = snd_pcm_slave_conf(root, slave, &sconf, 0); 793 if (err < 0) 794 return err; 795 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); 796 snd_config_delete(sconf); 797 if (err < 0) 798 return err; 799 err = snd_pcm_meter_open(pcmp, name, frequency > 0 ? (unsigned int) frequency : FREQUENCY, spcm, 1); 800 if (err < 0) { 801 snd_pcm_close(spcm); 802 return err; 803 } 804 if (!scopes) 805 return 0; 806 snd_config_for_each(i, next, scopes) { 807 snd_config_t *n = snd_config_iterator_entry(i); 808 const char *id, *str; 809 if (snd_config_get_id(n, &id) < 0) 810 continue; 811 if (snd_config_get_string(n, &str) >= 0) { 812 err = snd_config_search_definition(root, "pcm_scope", str, &n); 813 if (err < 0) { 814 SNDERR("unknown pcm_scope %s", str); 815 } else { 816 err = snd_pcm_meter_add_scope_conf(*pcmp, id, root, n); 817 snd_config_delete(n); 818 } 819 } else 820 err = snd_pcm_meter_add_scope_conf(*pcmp, id, root, n); 821 if (err < 0) { 822 snd_pcm_close(*pcmp); 823 return err; 824 } 825 } 826 return 0; 827 } 828 SND_DLSYM_BUILD_VERSION(_snd_pcm_meter_open, SND_PCM_DLSYM_VERSION); 829 830 #endif 831 832 /** 833 * \brief Add a scope to a #SND_PCM_TYPE_METER PCM 834 * \param pcm PCM handle 835 * \param scope Scope handle 836 * \return 0 on success otherwise a negative error code 837 */ 838 int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope) 839 { 840 snd_pcm_meter_t *meter; 841 assert(pcm->type == SND_PCM_TYPE_METER); 842 meter = pcm->private_data; 843 list_add_tail(&scope->list, &meter->scopes); 844 return 0; 845 } 846 847 /** 848 * \brief Search an installed scope inside a #SND_PCM_TYPE_METER PCM 849 * \param pcm PCM handle 850 * \param name scope name 851 * \return pointer to found scope or NULL if none is found 852 */ 853 snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name) 854 { 855 snd_pcm_meter_t *meter; 856 struct list_head *pos; 857 assert(pcm->type == SND_PCM_TYPE_METER); 858 meter = pcm->private_data; 859 list_for_each(pos, &meter->scopes) { 860 snd_pcm_scope_t *scope; 861 scope = list_entry(pos, snd_pcm_scope_t, list); 862 if (scope->name && strcmp(scope->name, name) == 0) 863 return scope; 864 } 865 return NULL; 866 } 867 868 /** 869 * \brief Get meter buffer size from a #SND_PCM_TYPE_METER PCM 870 * \param pcm PCM handle 871 * \return meter buffer size in frames 872 */ 873 snd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm) 874 { 875 snd_pcm_meter_t *meter; 876 assert(pcm->type == SND_PCM_TYPE_METER); 877 meter = pcm->private_data; 878 assert(meter->gen.slave->setup); 879 return meter->buf_size; 880 } 881 882 /** 883 * \brief Get meter channels from a #SND_PCM_TYPE_METER PCM 884 * \param pcm PCM handle 885 * \return meter channels count 886 */ 887 unsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm) 888 { 889 snd_pcm_meter_t *meter; 890 assert(pcm->type == SND_PCM_TYPE_METER); 891 meter = pcm->private_data; 892 assert(meter->gen.slave->setup); 893 return meter->gen.slave->channels; 894 } 895 896 /** 897 * \brief Get meter rate from a #SND_PCM_TYPE_METER PCM 898 * \param pcm PCM handle 899 * \return approximate rate 900 */ 901 unsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm) 902 { 903 snd_pcm_meter_t *meter; 904 assert(pcm->type == SND_PCM_TYPE_METER); 905 meter = pcm->private_data; 906 assert(meter->gen.slave->setup); 907 return meter->gen.slave->rate; 908 } 909 910 /** 911 * \brief Get meter "now" frame pointer from a #SND_PCM_TYPE_METER PCM 912 * \param pcm PCM handle 913 * \return "now" frame pointer in frames (0 ... boundary - 1) see #snd_pcm_meter_get_boundary 914 */ 915 snd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm) 916 { 917 snd_pcm_meter_t *meter; 918 assert(pcm->type == SND_PCM_TYPE_METER); 919 meter = pcm->private_data; 920 assert(meter->gen.slave->setup); 921 return meter->now; 922 } 923 924 /** 925 * \brief Get boundary for frame pointers from a #SND_PCM_TYPE_METER PCM 926 * \param pcm PCM handle 927 * \return boundary in frames 928 */ 929 snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm) 930 { 931 snd_pcm_meter_t *meter; 932 assert(pcm->type == SND_PCM_TYPE_METER); 933 meter = pcm->private_data; 934 assert(meter->gen.slave->setup); 935 return meter->gen.slave->boundary; 936 } 937 938 /** 939 * \brief Set name of a #SND_PCM_TYPE_METER PCM scope 940 * \param scope PCM meter scope 941 * \param val scope name 942 */ 943 void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val) 944 { 945 scope->name = strdup(val); 946 } 947 948 /** 949 * \brief Get name of a #SND_PCM_TYPE_METER PCM scope 950 * \param scope PCM meter scope 951 * \return scope name 952 */ 953 const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope) 954 { 955 return scope->name; 956 } 957 958 /** 959 * \brief Set callbacks for a #SND_PCM_TYPE_METER PCM scope 960 * \param scope PCM meter scope 961 * \param val callbacks 962 */ 963 void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, snd_pcm_scope_ops_t *val) 964 { 965 scope->ops = val; 966 } 967 968 /** 969 * \brief Get callbacks private value for a #SND_PCM_TYPE_METER PCM scope 970 * \param scope PCM meter scope 971 * \return Private data value 972 */ 973 void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope) 974 { 975 return scope->private_data; 976 } 977 978 /** 979 * \brief Get callbacks private value for a #SND_PCM_TYPE_METER PCM scope 980 * \param scope PCM meter scope 981 * \param val Private data value 982 */ 983 void snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val) 984 { 985 scope->private_data = val; 986 } 987 988 #ifndef DOC_HIDDEN 989 typedef struct _snd_pcm_scope_s16 { 990 snd_pcm_t *pcm; 991 snd_pcm_adpcm_state_t *adpcm_states; 992 unsigned int index; 993 snd_pcm_uframes_t old; 994 int16_t *buf; 995 snd_pcm_channel_area_t *buf_areas; 996 } snd_pcm_scope_s16_t; 997 998 static int s16_enable(snd_pcm_scope_t *scope) 999 { 1000 snd_pcm_scope_s16_t *s16 = scope->private_data; 1001 snd_pcm_meter_t *meter = s16->pcm->private_data; 1002 snd_pcm_t *spcm = meter->gen.slave; 1003 snd_pcm_channel_area_t *a; 1004 unsigned int c; 1005 int idx; 1006 if (spcm->format == SND_PCM_FORMAT_S16 && 1007 spcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) { 1008 s16->buf = (int16_t *) meter->buf; 1009 return -EINVAL; 1010 } 1011 switch (spcm->format) { 1012 case SND_PCM_FORMAT_A_LAW: 1013 case SND_PCM_FORMAT_MU_LAW: 1014 case SND_PCM_FORMAT_IMA_ADPCM: 1015 idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S16); 1016 break; 1017 case SND_PCM_FORMAT_S8: 1018 case SND_PCM_FORMAT_S16_LE: 1019 case SND_PCM_FORMAT_S16_BE: 1020 case SND_PCM_FORMAT_S24_LE: 1021 case SND_PCM_FORMAT_S24_BE: 1022 case SND_PCM_FORMAT_S32_LE: 1023 case SND_PCM_FORMAT_S32_BE: 1024 case SND_PCM_FORMAT_U8: 1025 case SND_PCM_FORMAT_U16_LE: 1026 case SND_PCM_FORMAT_U16_BE: 1027 case SND_PCM_FORMAT_U24_LE: 1028 case SND_PCM_FORMAT_U24_BE: 1029 case SND_PCM_FORMAT_U32_LE: 1030 case SND_PCM_FORMAT_U32_BE: 1031 idx = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16); 1032 break; 1033 default: 1034 return -EINVAL; 1035 } 1036 s16->index = idx; 1037 if (spcm->format == SND_PCM_FORMAT_IMA_ADPCM) { 1038 s16->adpcm_states = calloc(spcm->channels, sizeof(*s16->adpcm_states)); 1039 if (!s16->adpcm_states) 1040 return -ENOMEM; 1041 } 1042 s16->buf = malloc(meter->buf_size * 2 * spcm->channels); 1043 if (!s16->buf) { 1044 free(s16->adpcm_states); 1045 return -ENOMEM; 1046 } 1047 a = calloc(spcm->channels, sizeof(*a)); 1048 if (!a) { 1049 free(s16->buf); 1050 free(s16->adpcm_states); 1051 return -ENOMEM; 1052 } 1053 s16->buf_areas = a; 1054 for (c = 0; c < spcm->channels; c++, a++) { 1055 a->addr = s16->buf + c * meter->buf_size; 1056 a->first = 0; 1057 a->step = 16; 1058 } 1059 return 0; 1060 } 1061 1062 static void s16_disable(snd_pcm_scope_t *scope) 1063 { 1064 snd_pcm_scope_s16_t *s16 = scope->private_data; 1065 free(s16->adpcm_states); 1066 s16->adpcm_states = NULL; 1067 free(s16->buf); 1068 s16->buf = NULL; 1069 free(s16->buf_areas); 1070 s16->buf_areas = 0; 1071 } 1072 1073 static void s16_close(snd_pcm_scope_t *scope) 1074 { 1075 snd_pcm_scope_s16_t *s16 = scope->private_data; 1076 free(s16); 1077 } 1078 1079 static void s16_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED) 1080 { 1081 } 1082 1083 static void s16_stop(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED) 1084 { 1085 } 1086 1087 static void s16_update(snd_pcm_scope_t *scope) 1088 { 1089 snd_pcm_scope_s16_t *s16 = scope->private_data; 1090 snd_pcm_meter_t *meter = s16->pcm->private_data; 1091 snd_pcm_t *spcm = meter->gen.slave; 1092 snd_pcm_sframes_t size; 1093 snd_pcm_uframes_t offset; 1094 size = meter->now - s16->old; 1095 if (size < 0) 1096 size += spcm->boundary; 1097 offset = s16->old % meter->buf_size; 1098 while (size > 0) { 1099 snd_pcm_uframes_t frames = size; 1100 snd_pcm_uframes_t cont = meter->buf_size - offset; 1101 if (frames > cont) 1102 frames = cont; 1103 switch (spcm->format) { 1104 case SND_PCM_FORMAT_A_LAW: 1105 snd_pcm_alaw_decode(s16->buf_areas, offset, 1106 meter->buf_areas, offset, 1107 spcm->channels, frames, 1108 s16->index); 1109 break; 1110 case SND_PCM_FORMAT_MU_LAW: 1111 snd_pcm_mulaw_decode(s16->buf_areas, offset, 1112 meter->buf_areas, offset, 1113 spcm->channels, frames, 1114 s16->index); 1115 break; 1116 case SND_PCM_FORMAT_IMA_ADPCM: 1117 snd_pcm_adpcm_decode(s16->buf_areas, offset, 1118 meter->buf_areas, offset, 1119 spcm->channels, frames, 1120 s16->index, 1121 s16->adpcm_states); 1122 break; 1123 default: 1124 snd_pcm_linear_convert(s16->buf_areas, offset, 1125 meter->buf_areas, offset, 1126 spcm->channels, frames, 1127 s16->index); 1128 break; 1129 } 1130 if (frames == cont) 1131 offset = 0; 1132 else 1133 offset += frames; 1134 size -= frames; 1135 } 1136 s16->old = meter->now; 1137 } 1138 1139 static void s16_reset(snd_pcm_scope_t *scope) 1140 { 1141 snd_pcm_scope_s16_t *s16 = scope->private_data; 1142 snd_pcm_meter_t *meter = s16->pcm->private_data; 1143 s16->old = meter->now; 1144 } 1145 1146 static const snd_pcm_scope_ops_t s16_ops = { 1147 .enable = s16_enable, 1148 .disable = s16_disable, 1149 .close = s16_close, 1150 .start = s16_start, 1151 .stop = s16_stop, 1152 .update = s16_update, 1153 .reset = s16_reset, 1154 }; 1155 1156 #endif 1157 1158 /** 1159 * \brief Add a s16 pseudo scope to a #SND_PCM_TYPE_METER PCM 1160 * \param pcm The pcm handle 1161 * \param name Scope name 1162 * \param scopep Pointer to newly created and added scope 1163 * \return 0 on success otherwise a negative error code 1164 * 1165 * s16 pseudo scope convert #SND_PCM_TYPE_METER PCM frames in CPU endian 1166 * 16 bit frames for use with other scopes. Don't forget to insert it before 1167 * and to not insert it more time (see #snd_pcm_meter_search_scope) 1168 */ 1169 int snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name, 1170 snd_pcm_scope_t **scopep) 1171 { 1172 snd_pcm_meter_t *meter; 1173 snd_pcm_scope_t *scope; 1174 snd_pcm_scope_s16_t *s16; 1175 assert(pcm->type == SND_PCM_TYPE_METER); 1176 meter = pcm->private_data; 1177 scope = calloc(1, sizeof(*scope)); 1178 if (!scope) 1179 return -ENOMEM; 1180 s16 = calloc(1, sizeof(*s16)); 1181 if (!s16) { 1182 free(scope); 1183 return -ENOMEM; 1184 } 1185 if (name) 1186 scope->name = strdup(name); 1187 s16->pcm = pcm; 1188 scope->ops = &s16_ops; 1189 scope->private_data = s16; 1190 list_add_tail(&scope->list, &meter->scopes); 1191 *scopep = scope; 1192 return 0; 1193 } 1194 1195 /** 1196 * \brief Get s16 pseudo scope frames buffer for a channel 1197 * \param scope s16 pseudo scope handle 1198 * \param channel Channel 1199 * \return Pointer to channel buffer 1200 */ 1201 int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope, 1202 unsigned int channel) 1203 { 1204 snd_pcm_scope_s16_t *s16; 1205 snd_pcm_meter_t *meter; 1206 assert(scope->ops == &s16_ops); 1207 s16 = scope->private_data; 1208 meter = s16->pcm->private_data; 1209 assert(meter->gen.slave->setup); 1210 assert(s16->buf_areas); 1211 assert(channel < meter->gen.slave->channels); 1212 return s16->buf_areas[channel].addr; 1213 } 1214 1215 /** 1216 * \brief allocate an invalid #snd_pcm_scope_t using standard malloc 1217 * \param ptr returned pointer 1218 * \return 0 on success otherwise negative error code 1219 */ 1220 int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr) 1221 { 1222 assert(ptr); 1223 *ptr = calloc(1, sizeof(snd_pcm_scope_t)); 1224 if (!*ptr) 1225 return -ENOMEM; 1226 return 0; 1227 } 1228 1229