Blame src/pcm/scopes/level.c

Packit 4a16fb
/*
Packit 4a16fb
 *  PCM - Meter level plugin (ncurses)
Packit 4a16fb
 *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *
Packit 4a16fb
 *   This library is free software; you can redistribute it and/or modify
Packit 4a16fb
 *   it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
 *   published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
 *   the License, or (at your option) any later version.
Packit 4a16fb
 *
Packit 4a16fb
 *   This program is distributed in the hope that it will be useful,
Packit 4a16fb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
 *   GNU Lesser General Public License for more details.
Packit 4a16fb
 *
Packit 4a16fb
 *   You should have received a copy of the GNU Lesser General Public
Packit 4a16fb
 *   License along with this library; if not, write to the Free Software
Packit 4a16fb
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
Packit 4a16fb
#include <curses.h>
Packit 4a16fb
#include <errno.h>
Packit 4a16fb
#include <alsa/asoundlib.h>
Packit 4a16fb
Packit 4a16fb
#define BAR_WIDTH 70
Packit 4a16fb
/* milliseconds to go from 32767 to 0 */
Packit 4a16fb
#define DECAY_MS 400
Packit 4a16fb
/* milliseconds for peak to disappear */
Packit 4a16fb
#define PEAK_MS 800
Packit 4a16fb
Packit 4a16fb
typedef struct _snd_pcm_scope_level_channel {
Packit 4a16fb
	int16_t level;
Packit 4a16fb
	int16_t peak;
Packit 4a16fb
	unsigned int peak_age;
Packit 4a16fb
} snd_pcm_scope_level_channel_t;
Packit 4a16fb
Packit 4a16fb
typedef struct _snd_pcm_scope_level {
Packit 4a16fb
	snd_pcm_t *pcm;
Packit 4a16fb
	snd_pcm_scope_t *s16;
Packit 4a16fb
	snd_pcm_scope_level_channel_t *channels;
Packit 4a16fb
	snd_pcm_uframes_t old;
Packit 4a16fb
	int top;
Packit 4a16fb
	WINDOW *win;
Packit 4a16fb
	unsigned int bar_width;
Packit 4a16fb
	unsigned int decay_ms;
Packit 4a16fb
	unsigned int peak_ms;
Packit 4a16fb
} snd_pcm_scope_level_t;
Packit 4a16fb
Packit 4a16fb
static int level_enable(snd_pcm_scope_t *scope)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
Packit 4a16fb
	int y, x;
Packit 4a16fb
	level->channels = calloc(snd_pcm_meter_get_channels(level->pcm), sizeof(*level->channels));
Packit 4a16fb
	if (!level->channels) {
Packit 4a16fb
		free(level);
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	}
Packit 4a16fb
	snd_pcm_scope_set_callback_private(scope, level);
Packit 4a16fb
	level->win = initscr();
Packit 4a16fb
	winsdelln(level->win, snd_pcm_meter_get_channels(level->pcm));
Packit 4a16fb
        getyx(level->win, y, x);
Packit 4a16fb
	level->top = y;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void level_disable(snd_pcm_scope_t *scope)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
Packit 4a16fb
	endwin();
Packit 4a16fb
	free(level->channels);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void level_close(snd_pcm_scope_t *scope)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
Packit 4a16fb
	free(level);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void level_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
Packit 4a16fb
{
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void level_stop(snd_pcm_scope_t *scope)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
Packit 4a16fb
	unsigned int c;
Packit 4a16fb
	for (c = 0; c < snd_pcm_meter_get_channels(level->pcm); c++) {
Packit 4a16fb
		move(level->top + c, 0);
Packit 4a16fb
		clrtoeol();
Packit 4a16fb
	}
Packit 4a16fb
	move(level->top, 0);
Packit 4a16fb
	refresh();
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void level_update(snd_pcm_scope_t *scope)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
Packit 4a16fb
	snd_pcm_t *pcm = level->pcm;
Packit 4a16fb
	snd_pcm_sframes_t size;
Packit 4a16fb
	snd_pcm_uframes_t size1, size2;
Packit 4a16fb
	snd_pcm_uframes_t offset, cont;
Packit 4a16fb
	unsigned int c, channels;
Packit 4a16fb
	unsigned int ms;
Packit 4a16fb
	static char bar[256] = { [0 ... 255] = '#' };
Packit 4a16fb
	int max_decay;
Packit 4a16fb
	size = snd_pcm_meter_get_now(pcm) - level->old;
Packit 4a16fb
	if (size < 0)
Packit 4a16fb
		size += snd_pcm_meter_get_boundary(pcm);
Packit 4a16fb
	offset = level->old % snd_pcm_meter_get_bufsize(pcm);
Packit 4a16fb
	cont = snd_pcm_meter_get_bufsize(pcm) - offset;
Packit 4a16fb
	size1 = size;
Packit 4a16fb
	if (size1 > cont)
Packit 4a16fb
		size1 = cont;
Packit 4a16fb
	size2 = size - size1;
Packit 4a16fb
	ms = size * 1000 / snd_pcm_meter_get_rate(pcm);
Packit 4a16fb
	max_decay = 32768 * ms / level->decay_ms;
Packit 4a16fb
	channels = snd_pcm_meter_get_channels(pcm);
Packit 4a16fb
	for (c = 0; c < channels; c++) {
Packit 4a16fb
		int16_t *ptr;
Packit 4a16fb
		int s, lev = 0;
Packit 4a16fb
		snd_pcm_uframes_t n;
Packit 4a16fb
		snd_pcm_scope_level_channel_t *l;
Packit 4a16fb
		unsigned int lev_pos, peak_pos;
Packit 4a16fb
		l = &level->channels[c];
Packit 4a16fb
		ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c) + offset;
Packit 4a16fb
		for (n = size1; n > 0; n--) {
Packit 4a16fb
			s = *ptr;
Packit 4a16fb
			if (s < 0)
Packit 4a16fb
				s = -s;
Packit 4a16fb
			if (s > lev)
Packit 4a16fb
				lev = s;
Packit 4a16fb
			ptr++;
Packit 4a16fb
		}
Packit 4a16fb
		ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c);
Packit 4a16fb
		for (n = size2; n > 0; n--) {
Packit 4a16fb
			s = *ptr;
Packit 4a16fb
			if (s < 0)
Packit 4a16fb
				s = -s;
Packit 4a16fb
			if (s > lev)
Packit 4a16fb
				lev = s;
Packit 4a16fb
			ptr++;
Packit 4a16fb
		}
Packit 4a16fb
		l->level = lev;
Packit 4a16fb
		l->peak_age += ms;
Packit 4a16fb
		if (l->peak_age >= level->peak_ms ||
Packit 4a16fb
		    lev >= l->peak) {
Packit 4a16fb
			l->peak = lev;
Packit 4a16fb
			l->peak_age = 0;
Packit 4a16fb
		}
Packit 4a16fb
		if (lev < l->level - max_decay)
Packit 4a16fb
			lev = l->level - max_decay;
Packit 4a16fb
		move(level->top + c, 0);
Packit 4a16fb
		lev_pos = lev * level->bar_width / 32768;
Packit 4a16fb
		peak_pos = l->peak * level->bar_width / 32768;
Packit 4a16fb
		addnstr(bar, lev_pos);
Packit 4a16fb
		clrtoeol();
Packit 4a16fb
		mvaddch(level->top + c, peak_pos - 1, '#');
Packit 4a16fb
	}
Packit 4a16fb
	move(level->top, 0);
Packit 4a16fb
	refresh();
Packit 4a16fb
	level->old = snd_pcm_meter_get_now(pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void level_reset(snd_pcm_scope_t *scope)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
Packit 4a16fb
	snd_pcm_t *pcm = level->pcm;
Packit 4a16fb
	memset(level->channels, 0, snd_pcm_meter_get_channels(pcm) * sizeof(*level->channels));
Packit 4a16fb
	level->old = snd_pcm_meter_get_now(pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
snd_pcm_scope_ops_t level_ops = {
Packit 4a16fb
	.enable = level_enable,
Packit 4a16fb
	.disable = level_disable,
Packit 4a16fb
	.close = level_close,
Packit 4a16fb
	.start = level_start,
Packit 4a16fb
	.stop = level_stop,
Packit 4a16fb
	.update = level_update,
Packit 4a16fb
	.reset = level_reset,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
int snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name,
Packit 4a16fb
			     unsigned int bar_width, unsigned int decay_ms,
Packit 4a16fb
			     unsigned int peak_ms,
Packit 4a16fb
			     snd_pcm_scope_t **scopep)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_scope_t *scope, *s16;
Packit 4a16fb
	snd_pcm_scope_level_t *level;
Packit 4a16fb
	int err = snd_pcm_scope_malloc(&scope);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	level = calloc(1, sizeof(*level));
Packit 4a16fb
	if (!level) {
Packit 4a16fb
		free(scope);
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	}
Packit 4a16fb
	level->pcm = pcm;
Packit 4a16fb
	level->bar_width = bar_width;
Packit 4a16fb
	level->decay_ms = decay_ms;
Packit 4a16fb
	level->peak_ms = peak_ms;
Packit 4a16fb
	s16 = snd_pcm_meter_search_scope(pcm, "s16");
Packit 4a16fb
	if (!s16) {
Packit 4a16fb
		err = snd_pcm_scope_s16_open(pcm, "s16", &s16);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			free(scope);
Packit 4a16fb
			free(level);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	level->s16 = s16;
Packit 4a16fb
	snd_pcm_scope_set_ops(scope, &level_ops);
Packit 4a16fb
	snd_pcm_scope_set_callback_private(scope, level);
Packit 4a16fb
	if (name)
Packit 4a16fb
		snd_pcm_scope_set_name(scope, strdup(name));
Packit 4a16fb
	snd_pcm_meter_add_scope(pcm, scope);
Packit 4a16fb
	*scopep = scope;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
int _snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name,
Packit 4a16fb
			      snd_config_t *root, snd_config_t *conf)
Packit 4a16fb
{
Packit 4a16fb
	snd_config_iterator_t i, next;
Packit 4a16fb
	snd_pcm_scope_t *scope;
Packit 4a16fb
	long bar_width = -1, decay_ms = -1, peak_ms = -1;
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_config_for_each(i, next, conf) {
Packit 4a16fb
		snd_config_t *n = snd_config_iterator_entry(i);
Packit 4a16fb
		const char *id;
Packit 4a16fb
		if (snd_config_get_id(n, &id) < 0)
Packit 4a16fb
			continue;
Packit 4a16fb
		if (strcmp(id, "comment") == 0)
Packit 4a16fb
			continue;
Packit 4a16fb
		if (strcmp(id, "type") == 0)
Packit 4a16fb
			continue;
Packit 4a16fb
		if (strcmp(id, "bar_width") == 0) {
Packit 4a16fb
			err = snd_config_get_integer(n, &bar_width);
Packit 4a16fb
			if (err < 0) {
Packit 4a16fb
				SNDERR("Invalid type for %s", id);
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			}
Packit 4a16fb
			continue;
Packit 4a16fb
		}
Packit 4a16fb
		if (strcmp(id, "decay_ms") == 0) {
Packit 4a16fb
			err = snd_config_get_integer(n, &decay_ms);
Packit 4a16fb
			if (err < 0) {
Packit 4a16fb
				SNDERR("Invalid type for %s", id);
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			}
Packit 4a16fb
			continue;
Packit 4a16fb
		}
Packit 4a16fb
		if (strcmp(id, "peak_ms") == 0) {
Packit 4a16fb
			err = snd_config_get_integer(n, &peak_ms);
Packit 4a16fb
			if (err < 0) {
Packit 4a16fb
				SNDERR("Invalid type for %s", id);
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			}
Packit 4a16fb
			continue;
Packit 4a16fb
		}
Packit 4a16fb
		SNDERR("Unknown field %s", id);
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	if (bar_width < 0)
Packit 4a16fb
		bar_width = BAR_WIDTH;
Packit 4a16fb
	if (decay_ms < 0)
Packit 4a16fb
		decay_ms = DECAY_MS;
Packit 4a16fb
	if (peak_ms < 0)
Packit 4a16fb
		peak_ms = PEAK_MS;
Packit 4a16fb
	return snd_pcm_scope_level_open(pcm, name, bar_width, decay_ms, peak_ms,
Packit 4a16fb
					&scope);
Packit 4a16fb
}