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