• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  PCM - Meter level plugin (ncurses)
3  *  Copyright (c) 2001 by Abramo Bagnara <abramo@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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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 
level_enable(snd_pcm_scope_t * scope)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 
level_disable(snd_pcm_scope_t * scope)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 
level_close(snd_pcm_scope_t * scope)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 
level_start(snd_pcm_scope_t * scope ATTRIBUTE_UNUSED)79 static void level_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
80 {
81 }
82 
level_stop(snd_pcm_scope_t * scope)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 
level_update(snd_pcm_scope_t * scope)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 
level_reset(snd_pcm_scope_t * scope)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 
snd_pcm_scope_level_open(snd_pcm_t * pcm,const char * name,unsigned int bar_width,unsigned int decay_ms,unsigned int peak_ms,snd_pcm_scope_t ** scopep)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 
_snd_pcm_scope_level_open(snd_pcm_t * pcm,const char * name,snd_config_t * root,snd_config_t * conf)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