• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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