Lines Matching defs:*
2 * \file pcm/pcm_meter.c
3 * \brief Helper functions for #SND_PCM_TYPE_METER PCM scopes
4 * \author Abramo Bagnara <abramo@alsa-project.org>
5 * \date 2001
7 * Helper functions for #SND_PCM_TYPE_METER PCM scopes
10 * PCM - Meter plugin
11 * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
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.
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.
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
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"
38 #ifndef PIC
39 /* entry for static linking */
40 const char *_snd_module_pcm_meter = "";
41 #endif
43 #ifndef DOC_HIDDEN
44 #define FREQUENCY 50
46 struct _snd_pcm_scope {
47 int enabled;
48 char *name;
49 const snd_pcm_scope_ops_t *ops;
50 void *private_data;
51 struct list_head list;
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;
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)
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;
99 static void snd_pcm_meter_update_main(snd_pcm_t *pcm)
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);
119 if (locked)
120 pthread_mutex_unlock(&meter->update_mutex);
123 static int snd_pcm_meter_update_scope(snd_pcm_t *pcm)
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;
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);
151 pthread_mutex_unlock(&meter->update_mutex);
152 return reset;
155 static int snd_pcm_scope_remove(snd_pcm_scope_t *scope)
157 free(scope->name);
158 scope->ops->close(scope);
159 list_del(&scope->list);
160 free(scope);
161 return 0;
164 static int snd_pcm_scope_enable(snd_pcm_scope_t *scope)
166 int err;
167 assert(!scope->enabled);
168 err = scope->ops->enable(scope);
169 scope->enabled = (err >= 0);
170 return err;
173 static int snd_pcm_scope_disable(snd_pcm_scope_t *scope)
175 assert(scope->enabled);
176 scope->ops->disable(scope);
177 scope->enabled = 0;
178 return 0;
181 static void *snd_pcm_meter_thread(void *data)
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);
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);
208 meter->running = 0;
210 pthread_cond_wait(&meter->running_cond,
211 &meter->running_mutex);
212 pthread_mutex_unlock(&meter->running_mutex);
213 continue;
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;
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);
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);
241 continue;
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);
249 meter->running = 1;
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);
256 nanosleep(&meter->delay, NULL);
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);
263 return NULL;
266 static int snd_pcm_meter_close(snd_pcm_t *pcm)
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);
281 if (meter->dl_handle)
282 snd_dlclose(meter->dl_handle);
283 free(meter);
284 return err;
287 static int snd_pcm_meter_prepare(snd_pcm_t *pcm)
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;
299 return err;
302 static int snd_pcm_meter_reset(snd_pcm_t *pcm)
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;
310 return err;
313 static int snd_pcm_meter_start(snd_pcm_t *pcm)
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;
325 static snd_pcm_sframes_t snd_pcm_meter_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
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;
334 static snd_pcm_sframes_t snd_pcm_meter_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
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;
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)
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;
356 return result;
359 static snd_pcm_sframes_t snd_pcm_meter_avail_update(snd_pcm_t *pcm)
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;
370 static int snd_pcm_meter_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
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;
382 static int snd_pcm_meter_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)
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;
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)
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;
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)
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;
413 static int snd_pcm_meter_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
415 snd_pcm_meter_t *meter = pcm->private_data;
416 return snd_pcm_hw_refine(meter->gen.slave, params);
419 static int snd_pcm_meter_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
421 snd_pcm_meter_t *meter = pcm->private_data;
422 return _snd_pcm_hw_params(meter->gen.slave, params);
425 static int snd_pcm_meter_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
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);
435 static int snd_pcm_meter_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
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;
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;
469 meter->closed = 0;
470 err = pthread_create(&meter->thread, NULL, snd_pcm_meter_thread, pcm);
471 assert(err == 0);
472 return 0;
475 static int snd_pcm_meter_hw_free(snd_pcm_t *pcm)
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);
492 static void snd_pcm_meter_dump(snd_pcm_t *pcm, snd_output_t *out)
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);
500 snd_output_printf(out, "Slave: ");
501 snd_pcm_dump(meter->gen.slave, out);
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,
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,
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.
559 int snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, unsigned int frequency,
560 snd_pcm_t *slave, int close_slave)
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);
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;
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;
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;
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)
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;
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;
618 err = snd_config_search(conf, "type", &c);
619 if (err < 0) {
620 SNDERR("type is not defined");
621 goto _err;
623 err = snd_config_get_id(c, &id);
624 if (err < 0) {
625 SNDERR("unable to get id");
626 goto _err;
628 err = snd_config_get_string(c, &str);
629 if (err < 0) {
630 SNDERR("Invalid type for %s", id);
631 goto _err;
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;
648 continue;
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;
656 continue;
658 SNDERR("Unknown field %s", id);
659 err = -EINVAL;
660 goto _err;
663 if (!open_name) {
664 open_name = buf;
665 snprintf(buf, sizeof(buf), "_snd_pcm_scope_%s_open", str);
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;
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;
688 return err;
691 /*! \page pcm_plugins
693 \section pcm_plugins_meter Plugin: Meter
695 Show meter (visual waveform representation).
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)
703 pcm_scope.name {
704 type STR # Scope type
705 ...
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
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)
724 \endcode
726 \subsection pcm_plugins_meter_funcref Function reference
728 <UL>
729 <LI>snd_pcm_meter_open()
730 <LI>_snd_pcm_meter_open()
731 </UL>
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.
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)
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;
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;
775 continue;
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;
782 scopes = n;
783 continue;
785 SNDERR("Unknown field %s", id);
786 return -EINVAL;
788 if (!slave) {
789 SNDERR("slave is not defined");
790 return -EINVAL;
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;
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);
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;
826 return 0;
828 SND_DLSYM_BUILD_VERSION(_snd_pcm_meter_open, SND_PCM_DLSYM_VERSION);
830 #endif
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
838 int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope)
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;
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
853 snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name)
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;
865 return NULL;
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
873 snd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm)
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;
883 * \brief Get meter channels from a #SND_PCM_TYPE_METER PCM
884 * \param pcm PCM handle
885 * \return meter channels count
887 unsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm)
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;
897 * \brief Get meter rate from a #SND_PCM_TYPE_METER PCM
898 * \param pcm PCM handle
899 * \return approximate rate
901 unsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm)
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;
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
915 snd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm)
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;
925 * \brief Get boundary for frame pointers from a #SND_PCM_TYPE_METER PCM
926 * \param pcm PCM handle
927 * \return boundary in frames
929 snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm)
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;
939 * \brief Set name of a #SND_PCM_TYPE_METER PCM scope
940 * \param scope PCM meter scope
941 * \param val scope name
943 void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val)
945 scope->name = strdup(val);
949 * \brief Get name of a #SND_PCM_TYPE_METER PCM scope
950 * \param scope PCM meter scope
951 * \return scope name
953 const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope)
955 return scope->name;
959 * \brief Set callbacks for a #SND_PCM_TYPE_METER PCM scope
960 * \param scope PCM meter scope
961 * \param val callbacks
963 void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, const snd_pcm_scope_ops_t *val)
965 scope->ops = val;
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
973 void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope)
975 return scope->private_data;
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
983 void snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val)
985 scope->private_data = val;
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;
998 static int s16_enable(snd_pcm_scope_t *scope)
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;
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;
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;
1042 s16->buf = malloc(meter->buf_size * 2 * spcm->channels);
1043 if (!s16->buf) {
1044 free(s16->adpcm_states);
1045 return -ENOMEM;
1047 a = calloc(spcm->channels, sizeof(*a));
1048 if (!a) {
1049 free(s16->buf);
1050 free(s16->adpcm_states);
1051 return -ENOMEM;
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;
1059 return 0;
1062 static void s16_disable(snd_pcm_scope_t *scope)
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;
1073 static void s16_close(snd_pcm_scope_t *scope)
1075 snd_pcm_scope_s16_t *s16 = scope->private_data;
1076 free(s16);
1079 static void s16_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
1083 static void s16_stop(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
1087 static void s16_update(snd_pcm_scope_t *scope)
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;
1130 if (frames == cont)
1131 offset = 0;
1132 else
1133 offset += frames;
1134 size -= frames;
1136 s16->old = meter->now;
1139 static void s16_reset(snd_pcm_scope_t *scope)
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;
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,
1156 #endif
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
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)
1169 int snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name,
1170 snd_pcm_scope_t **scopep)
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;
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;
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
1201 int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope,
1202 unsigned int channel)
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;
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
1220 int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr)
1222 assert(ptr);
1223 *ptr = calloc(1, sizeof(snd_pcm_scope_t));
1224 if (!*ptr)
1225 return -ENOMEM;
1226 return 0;