/**************************************************************************
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright 1992 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/
/****************************************************************************
*
* rlmeter.c: Audio recording level window
*
* Vidcap32 Source code
*
***************************************************************************/
/*
* This window class acts as a 'VU Meter' showing the current and peak
* volume. Set the volume via the WMRL_SETLEVEL message (lParam is new level).
* The peak level will be tracked by the control by means of a 2-second timer.
*/
#include <windows.h>
#include <windowsx.h>
#include "rlmeter.h"
#ifdef _WIN32
#ifndef EXPORT
#define EXPORT
#endif
#endif
LONG FAR PASCAL EXPORT
RLMeterProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
/*
* generic window class to support a volume level display.
*
* The window has a white background, and draws a black filled
* rectangle to show the current volume level, and a red line at the
* peak. Every two seconds on a timer we lower the peak (we set the
* saved peak value to 0 so that at the next update we move the line to
* whatever is the current level.
*
* We store the pen and brush handles and the current and maximum levels
* as window words using SetWindowWord on win16 and SetWindowLong on win32.
*/
// window data layout
#define WD_MAX 0 // current max
#define WD_PREVMAX (WD_MAX + sizeof(UINT)) // currently drawn max
#define WD_PREVLVL (WD_PREVMAX + sizeof(UINT)) // currently drawn level
#define WD_PEN (WD_PREVLVL + sizeof(UINT)) // pen for max line
#define WDBYTES (WD_PEN + sizeof(UINT)) // window bytes to alloc
#ifdef _WIN32
#define SetWindowUINT SetWindowLong
#define GetWindowUINT GetWindowLong
#else
#define SetWindowUINT SetWindowWord
#define GetWindowUINT GetWindowWord
#endif
// call (if first instance) to register class
BOOL
RLMeter_Register(HINSTANCE hInstance)
{
WNDCLASS cls;
cls.hCursor = LoadCursor(NULL,IDC_ARROW);
cls.hIcon = NULL;
cls.lpszMenuName = NULL;
cls.lpszClassName = RLMETERCLASS;
cls.hbrBackground = GetStockObject(WHITE_BRUSH);
cls.hInstance = hInstance;
cls.style = CS_HREDRAW | CS_VREDRAW;
cls.lpfnWndProc = RLMeterProc;
cls.cbClsExtra = 0;
cls.cbWndExtra = WDBYTES;
return RegisterClass(&cls);
}
LONG FAR PASCAL EXPORT
RLMeterProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message) {
case WM_CREATE:
// init current level and max to 0
SetWindowUINT(hwnd, WD_MAX, 0);
SetWindowUINT(hwnd, WD_PREVMAX, 0);
SetWindowUINT(hwnd, WD_PREVLVL, 0);
// create a red pen for the max line and store this
SetWindowUINT(hwnd, WD_PEN,
(UINT) CreatePen(PS_SOLID, 2, RGB(255, 0, 0)));
break;
case WM_DESTROY:
// destroy the pen we created
{
HPEN hpen = (HPEN) GetWindowUINT(hwnd, WD_PEN);
if (hpen) {
DeleteObject(hpen);
SetWindowUINT(hwnd, WD_PEN, 0);
}
// also kill the timer we created
KillTimer(hwnd, 0);
}
break;
case WM_PAINT:
/*
* paint the entire control
*
* nb we must paint exactly as it is currently drawn because we
* may be clipped to only part of the control. Thus we must draw
* the max at WD_PREVMAX as it is currently drawn, since WD_MAX
* may have been set to 0 and not yet drawn - in this case, with
* some unfortunate timing and clipping, we would have two max lines.
*/
{
PAINTSTRUCT ps;
HDC hdc;
RECT rc, rcFill;
HPEN hpenOld, hpen;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
// treat the level as a percentage and fill that much of the
// control with black (from left)
rcFill = rc;
rcFill.right = (rc.right * GetWindowUINT(hwnd, WD_PREVLVL)) / 100;
SetBkColor(hdc, RGB(0,0,0));
// easy way to fill without creating a brush
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcFill, NULL, 0, NULL);
// draw the max line
rcFill.right = (rc.right * GetWindowUINT(hwnd, WD_PREVLVL)) / 100;
hpen = (HPEN) GetWindowUINT(hwnd, WD_PEN);
hpenOld = SelectObject(hdc, hpen);
MoveToEx(hdc, rcFill.right, rcFill.top, NULL);
LineTo(hdc, rcFill.right, rcFill.bottom);
SelectObject(hdc, hpenOld);
EndPaint(hwnd, &ps);
}
break;
case WMRL_SETLEVEL:
// set new level, and update the displayed level block and max line
{
RECT rc, rcFill;
UINT uMax, uPrevMax, uPrevLevel, uLevel;
HDC hdc;
// new level is lParam
uLevel = (UINT) lParam;
// fetch other parameters
uMax = GetWindowUINT(hwnd, WD_MAX);
uPrevMax = GetWindowUINT(hwnd, WD_PREVMAX);
uPrevLevel = GetWindowUINT(hwnd, WD_PREVLVL);
// decay the max level. This rate works best if we are called
// to update every 1/20th sec - in this case the decay will be
// 64% in a second.
if (uMax > 0) {
uMax = (uMax * 2007) / 2048; // = 0.98 * uMax
}
hdc = GetDC(hwnd);
GetClientRect(hwnd, &rc);
rcFill = rc;
// is the current level a new peak ?
if (uLevel > uMax) {
uMax = uLevel;
}
SetWindowUINT(hwnd, WD_MAX, uMax);
// if the max has moved, erase the old line
if (uMax != uPrevMax) {
// white out the line by filling a 2-pixel wide rect
rcFill.right = ((rc.right * uPrevMax) / 100) + 1;
rcFill.left = rcFill.right - 2;
SetBkColor(hdc, RGB(255, 255, 255));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcFill, NULL, 0, NULL);
}
// calculate the area to update
rcFill.right = (rc.right * uPrevLevel) / 100;
rcFill.left = (rc.right * uLevel) / 100;
// are we erasing (lowering level) or drawing more black?
if (rcFill.right > rcFill.left) {
// level has dropped - so fill with white down to new level
SetBkColor(hdc, RGB(255, 255, 255));
} else {
// level has gone up so fill with black up to new level
int t;
t = rcFill.right;
rcFill.right = rcFill.left;
rcFill.left = t;
SetBkColor(hdc, RGB(0, 0, 0));
// fill a little extra to ensure no rounding gaps
if (rcFill.left > 0) {
rcFill.left -= 1;
}
}
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcFill, NULL, 0, NULL);
SetWindowUINT(hwnd, WD_PREVLVL, uLevel);
// draw the new max line if needed
if (uMax != uPrevMax) {
HPEN hpen, hpenOld;
rcFill.right = (rc.right * uMax) /100;
hpen = (HPEN) GetWindowUINT(hwnd, WD_PEN);
hpenOld = SelectObject(hdc, hpen);
MoveToEx(hdc, rcFill.right, rcFill.top, NULL);
LineTo(hdc, rcFill.right, rcFill.bottom);
SelectObject(hdc, hpenOld);
SetWindowUINT(hwnd, WD_PREVMAX, uMax);
}
ReleaseDC(hwnd, hdc);
return(0);
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}