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