/************************************************************************** * * 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 (c) 1992 - 1995 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 #include #include "rlmeter.h" #ifdef _WIN32 #ifndef EXPORT #define EXPORT #endif #endif LRESULT 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_PTR)) // window bytes to alloc #ifdef _WIN32 #define SetWindowUINT SetWindowLong #define GetWindowUINT GetWindowLong #define SetWindowUINTPtr SetWindowLongPtr #define GetWindowUINTPtr GetWindowLongPtr #else #define SetWindowUINT SetWindowWord #define GetWindowUINT GetWindowWord #define SetWindowUINTPtr SetWindowWord #define GetWindowUINTPtr 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); } LRESULT 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 SetWindowUINTPtr(hwnd, WD_PEN, (UINT_PTR) CreatePen(PS_SOLID, 2, RGB(255, 0, 0))); break; case WM_DESTROY: // destroy the pen we created { HPEN hpen = (HPEN) GetWindowUINTPtr(hwnd, WD_PEN); if (hpen) { DeleteObject(hpen); SetWindowUINTPtr(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) GetWindowUINTPtr(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) GetWindowUINTPtr(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); }