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