Home | History | Annotate | Download | only in scopes
      1 /*
      2  *  PCM - Meter level plugin (ncurses)
      3  *  Copyright (c) 2001 by Abramo Bagnara <abramo (at) alsa-project.org>
      4  *
      5  *   This library is free software; you can redistribute it and/or modify
      6  *   it under the terms of the GNU Lesser General Public License as
      7  *   published by the Free Software Foundation; either version 2.1 of
      8  *   the License, or (at your option) any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *   GNU Lesser General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU Lesser General Public
     16  *   License along with this library; if not, write to the Free Software
     17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     18  *
     19  */
     20 
     21 #include <curses.h>
     22 #include <errno.h>
     23 #include <alsa/asoundlib.h>
     24 
     25 #define BAR_WIDTH 70
     26 /* milliseconds to go from 32767 to 0 */
     27 #define DECAY_MS 400
     28 /* milliseconds for peak to disappear */
     29 #define PEAK_MS 800
     30 
     31 typedef struct _snd_pcm_scope_level_channel {
     32 	int16_t level;
     33 	int16_t peak;
     34 	unsigned int peak_age;
     35 } snd_pcm_scope_level_channel_t;
     36 
     37 typedef struct _snd_pcm_scope_level {
     38 	snd_pcm_t *pcm;
     39 	snd_pcm_scope_t *s16;
     40 	snd_pcm_scope_level_channel_t *channels;
     41 	snd_pcm_uframes_t old;
     42 	int top;
     43 	WINDOW *win;
     44 	unsigned int bar_width;
     45 	unsigned int decay_ms;
     46 	unsigned int peak_ms;
     47 } snd_pcm_scope_level_t;
     48 
     49 static int level_enable(snd_pcm_scope_t *scope)
     50 {
     51 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
     52 	int y, x;
     53 	level->channels = calloc(snd_pcm_meter_get_channels(level->pcm), sizeof(*level->channels));
     54 	if (!level->channels) {
     55 		free(level);
     56 		return -ENOMEM;
     57 	}
     58 	snd_pcm_scope_set_callback_private(scope, level);
     59 	level->win = initscr();
     60 	winsdelln(level->win, snd_pcm_meter_get_channels(level->pcm));
     61         getyx(level->win, y, x);
     62 	level->top = y;
     63 	return 0;
     64 }
     65 
     66 static void level_disable(snd_pcm_scope_t *scope)
     67 {
     68 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
     69 	endwin();
     70 	free(level->channels);
     71 }
     72 
     73 static void level_close(snd_pcm_scope_t *scope)
     74 {
     75 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
     76 	free(level);
     77 }
     78 
     79 static void level_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
     80 {
     81 }
     82 
     83 static void level_stop(snd_pcm_scope_t *scope)
     84 {
     85 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
     86 	unsigned int c;
     87 	for (c = 0; c < snd_pcm_meter_get_channels(level->pcm); c++) {
     88 		move(level->top + c, 0);
     89 		clrtoeol();
     90 	}
     91 	move(level->top, 0);
     92 	refresh();
     93 }
     94 
     95 static void level_update(snd_pcm_scope_t *scope)
     96 {
     97 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
     98 	snd_pcm_t *pcm = level->pcm;
     99 	snd_pcm_sframes_t size;
    100 	snd_pcm_uframes_t size1, size2;
    101 	snd_pcm_uframes_t offset, cont;
    102 	unsigned int c, channels;
    103 	unsigned int ms;
    104 	static char bar[256] = { [0 ... 255] = '#' };
    105 	int max_decay;
    106 	size = snd_pcm_meter_get_now(pcm) - level->old;
    107 	if (size < 0)
    108 		size += snd_pcm_meter_get_boundary(pcm);
    109 	offset = level->old % snd_pcm_meter_get_bufsize(pcm);
    110 	cont = snd_pcm_meter_get_bufsize(pcm) - offset;
    111 	size1 = size;
    112 	if (size1 > cont)
    113 		size1 = cont;
    114 	size2 = size - size1;
    115 	ms = size * 1000 / snd_pcm_meter_get_rate(pcm);
    116 	max_decay = 32768 * ms / level->decay_ms;
    117 	channels = snd_pcm_meter_get_channels(pcm);
    118 	for (c = 0; c < channels; c++) {
    119 		int16_t *ptr;
    120 		int s, lev = 0;
    121 		snd_pcm_uframes_t n;
    122 		snd_pcm_scope_level_channel_t *l;
    123 		unsigned int lev_pos, peak_pos;
    124 		l = &level->channels[c];
    125 		ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c) + offset;
    126 		for (n = size1; n > 0; n--) {
    127 			s = *ptr;
    128 			if (s < 0)
    129 				s = -s;
    130 			if (s > lev)
    131 				lev = s;
    132 			ptr++;
    133 		}
    134 		ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c);
    135 		for (n = size2; n > 0; n--) {
    136 			s = *ptr;
    137 			if (s < 0)
    138 				s = -s;
    139 			if (s > lev)
    140 				lev = s;
    141 			ptr++;
    142 		}
    143 		l->level = lev;
    144 		l->peak_age += ms;
    145 		if (l->peak_age >= level->peak_ms ||
    146 		    lev >= l->peak) {
    147 			l->peak = lev;
    148 			l->peak_age = 0;
    149 		}
    150 		if (lev < l->level - max_decay)
    151 			lev = l->level - max_decay;
    152 		move(level->top + c, 0);
    153 		lev_pos = lev * level->bar_width / 32768;
    154 		peak_pos = l->peak * level->bar_width / 32768;
    155 		addnstr(bar, lev_pos);
    156 		clrtoeol();
    157 		mvaddch(level->top + c, peak_pos - 1, '#');
    158 	}
    159 	move(level->top, 0);
    160 	refresh();
    161 	level->old = snd_pcm_meter_get_now(pcm);
    162 }
    163 
    164 static void level_reset(snd_pcm_scope_t *scope)
    165 {
    166 	snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
    167 	snd_pcm_t *pcm = level->pcm;
    168 	memset(level->channels, 0, snd_pcm_meter_get_channels(pcm) * sizeof(*level->channels));
    169 	level->old = snd_pcm_meter_get_now(pcm);
    170 }
    171 
    172 snd_pcm_scope_ops_t level_ops = {
    173 	.enable = level_enable,
    174 	.disable = level_disable,
    175 	.close = level_close,
    176 	.start = level_start,
    177 	.stop = level_stop,
    178 	.update = level_update,
    179 	.reset = level_reset,
    180 };
    181 
    182 int snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name,
    183 			     unsigned int bar_width, unsigned int decay_ms,
    184 			     unsigned int peak_ms,
    185 			     snd_pcm_scope_t **scopep)
    186 {
    187 	snd_pcm_scope_t *scope, *s16;
    188 	snd_pcm_scope_level_t *level;
    189 	int err = snd_pcm_scope_malloc(&scope);
    190 	if (err < 0)
    191 		return err;
    192 	level = calloc(1, sizeof(*level));
    193 	if (!level) {
    194 		free(scope);
    195 		return -ENOMEM;
    196 	}
    197 	level->pcm = pcm;
    198 	level->bar_width = bar_width;
    199 	level->decay_ms = decay_ms;
    200 	level->peak_ms = peak_ms;
    201 	s16 = snd_pcm_meter_search_scope(pcm, "s16");
    202 	if (!s16) {
    203 		err = snd_pcm_scope_s16_open(pcm, "s16", &s16);
    204 		if (err < 0) {
    205 			free(scope);
    206 			free(level);
    207 			return err;
    208 		}
    209 	}
    210 	level->s16 = s16;
    211 	snd_pcm_scope_set_ops(scope, &level_ops);
    212 	snd_pcm_scope_set_callback_private(scope, level);
    213 	if (name)
    214 		snd_pcm_scope_set_name(scope, strdup(name));
    215 	snd_pcm_meter_add_scope(pcm, scope);
    216 	*scopep = scope;
    217 	return 0;
    218 }
    219 
    220 int _snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name,
    221 			      snd_config_t *root, snd_config_t *conf)
    222 {
    223 	snd_config_iterator_t i, next;
    224 	snd_pcm_scope_t *scope;
    225 	long bar_width = -1, decay_ms = -1, peak_ms = -1;
    226 	int err;
    227 	snd_config_for_each(i, next, conf) {
    228 		snd_config_t *n = snd_config_iterator_entry(i);
    229 		const char *id;
    230 		if (snd_config_get_id(n, &id) < 0)
    231 			continue;
    232 		if (strcmp(id, "comment") == 0)
    233 			continue;
    234 		if (strcmp(id, "type") == 0)
    235 			continue;
    236 		if (strcmp(id, "bar_width") == 0) {
    237 			err = snd_config_get_integer(n, &bar_width);
    238 			if (err < 0) {
    239 				SNDERR("Invalid type for %s", id);
    240 				return -EINVAL;
    241 			}
    242 			continue;
    243 		}
    244 		if (strcmp(id, "decay_ms") == 0) {
    245 			err = snd_config_get_integer(n, &decay_ms);
    246 			if (err < 0) {
    247 				SNDERR("Invalid type for %s", id);
    248 				return -EINVAL;
    249 			}
    250 			continue;
    251 		}
    252 		if (strcmp(id, "peak_ms") == 0) {
    253 			err = snd_config_get_integer(n, &peak_ms);
    254 			if (err < 0) {
    255 				SNDERR("Invalid type for %s", id);
    256 				return -EINVAL;
    257 			}
    258 			continue;
    259 		}
    260 		SNDERR("Unknown field %s", id);
    261 		return -EINVAL;
    262 	}
    263 	if (bar_width < 0)
    264 		bar_width = BAR_WIDTH;
    265 	if (decay_ms < 0)
    266 		decay_ms = DECAY_MS;
    267 	if (peak_ms < 0)
    268 		peak_ms = PEAK_MS;
    269 	return snd_pcm_scope_level_open(pcm, name, bar_width, decay_ms, peak_ms,
    270 					&scope);
    271 }
    272