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