/* * Copyright (c) 1990-1994 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static const char rcsid[] = "@(#) $Header: vu.cc,v 1.16 96/03/16 13:12:55 van Exp $ (LBL)"; #include "config.h" #include #include "vu.h" #include "Tcl.h" int VUMeter::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "set") == 0) { set(atof(argv[2])); redraw(); return (TCL_OK); } } return (TkWidget::command(argc, argv)); } void VUMeter::resize() { topblk_ = peak_ = 0; setpeak_ = 0; peakcnt_ = 0; ntop_ = 0; nlevel_ = int(height_ / barht_); const char* hotLevel = Tk_GetOption(tk_, "hotLevel", "VatVU"); hot_level_ = nlevel_ * atoi(hotLevel) / 100; } inline void VUMeter::rect(Display* dpy, Drawable win, GC gc, int x, int y, int w, int h) const { XFillRectangle(dpy, win, gc, x, height_ - y - h, w, h); } void VUMeter::draw() { Display* dpy = Tk_Display(tk_); Drawable window = Tk_WindowId(tk_); int w = width_ - 2 * gap_; rect(dpy, window, bg_, 0, 0, width_, height_); int y = 0; for (int i = 0; i < topblk_; ++i) { rect(dpy, window, fg_, gap_, y, w, barht_ - gap_); y += barht_; } if (peak_ > 0) { y = (peak_ - 1) * barht_; GC gc = (peak_ >= hot_level_) ? hot_ : gray_; rect(dpy, window, gc, gap_, y, w, barht_ - gap_); } } void VUMeter::update() { int w = width_ - 2 * gap_; int lblk = topblk_; int tblk = ntop_; int dif = tblk - topblk_; if (dif == 0) return; topblk_ = tblk; Display* dpy = Tk_Display(tk_); Drawable window = Tk_WindowId(tk_); if (dif < 0) { /* lblk is above tblk */ int ymin = tblk * barht_; int ymax = lblk * barht_; if (lblk == peak_) ymax -= barht_; int h = ymax - ymin; if (h > 0) rect(dpy, window, bg_, gap_, ymin, w, h); if (setpeak_) { if (tblk == 0) return; setpeak_ = 0; int y = (peak_ - 1) * barht_; rect(dpy, window, bg_, gap_, y, w, barht_ - gap_); peak_ = tblk; y = (tblk - 1) * barht_; if (mono_) /* * For mono, need to clear background * first since we do a stippled fill. */ rect(dpy, window, bg_, gap_, y, w, barht_ - gap_); GC gc = (tblk >= hot_level_) ? hot_ : gray_; rect(dpy, window, gc, gap_, y, w, barht_ - gap_); } return; } if (peak_ < tblk) { setpeak_ = 0; peak_ = tblk; int y = (tblk - 1) * barht_; GC gc = (tblk >= hot_level_) ? hot_ : gray_; rect(dpy, window, gc, gap_, y, w, barht_ - gap_); } if (tblk == peak_) --tblk; if (lblk > 0) /* * Start one lower than we have to since the old * top block might be the peak indicator. */ --lblk; int y = lblk * barht_; for (int i = lblk + 1; i <= tblk; ++i) { rect(dpy, window, fg_, gap_, y, w, barht_ - gap_); y += barht_; } } GC VUMeter::bg_; GC VUMeter::fg_; GC VUMeter::gray_; GC VUMeter::hot_; VUMeter::VUMeter(const char* path, double s, double f, int width) : TkWidget(path, "VatVU", width, 100), value_(0.), alpha_(f), scale_(s), barht_(width / 2), gap_(2), nlevel_(0), hot_level_(0), topblk_(0), ntop_(0), peak_(0), peakcnt_(0), setpeak_(0) { if (bg_ == 0) { Tk_Uid bg = Tk_GetOption(tk_, "background", "VatVU"); Tk_Uid fg = Tk_GetOption(tk_, "foreground", "VatVU"); Tk_Uid gr = Tk_GetOption(tk_, "peak", "VatVU"); Tk_Uid hot = Tk_GetOption(tk_, "hot", "VatVU"); bg_ = lookup_gc(0, bg, bg); fg_ = lookup_gc(0, fg, bg); gray_ = lookup_gc(0, gr, bg); hot_ = lookup_gc(0, hot, bg); if (hot_ == 0) /*XXX error message */ hot_ = fg_; } } void VUMeter::set(double d) { d = warp(d); d *= scale_; if (d > 1.0) d = 1.0; else if (d < 0.0) d = 0.0; register double v = value_; v += alpha_ * (d - v); value_ = v; int nblk = int(double(nlevel_) * v + 0.5); if (nblk != ntop_) { ntop_ = nblk; if (--peakcnt_ < 0) { peakcnt_ = 25; setpeak_ = 1; } /* * Call update() here instead of redraw() since the meter * needs to be animated in real-time. Using redraw * makes for very sluggish action. */ if (Tk_IsMapped(tk_)) { #ifdef notdef /*XXX can't do this because damage_ is private */ damage_ = 0; #endif update(); } } } DBMeter::DBMeter(const char* path, double scale, double lpfgain, int width) : VUMeter(path, 1 / log10(scale), lpfgain, width) { } double DBMeter::warp(double d) const { if (d != 0.) { if (d < 0) d = -d; d = log10(d); } return (d); } LinearMeter::LinearMeter(const char* path, double scale, double lpfgain, int width) : VUMeter(path, scale, lpfgain, width) { } double LinearMeter::warp(double d) const { if (d < 0) d = -d; return (d); } static class MeterCommand : public TclObject { public: MeterCommand() : TclObject("meter") {} virtual int command(int argc, const char*const* argv); } meter_command; int MeterCommand::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { /* * the value '81' for meter full scale is because we want to * peg the meter if the peak amplitude of some tone hits the * u-law clipping level (127). Since the mean of abs(sin(x)) * over an integral number of periods is 2/pi, the meter * fullscale is set to 127*2/3.14 = 81. */ LinearMeter* p = new LinearMeter(argv[1], 1./81., 0.75, 20); tcl.result(p->name()); return (TCL_OK); } return (TclObject::command(argc, argv)); }