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