You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
8.5 KiB
262 lines
8.5 KiB
/**************************************************************************
|
|
*
|
|
* 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 <windows.h>
|
|
#include <windowsx.h>
|
|
|
|
#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);
|
|
}
|
|
|
|
|
|
|