PeakMeterCtrl.cpp
上传用户:amei960
上传日期:2007-02-05
资源大小:143k
文件大小:18k
源码类别:

Audio

开发平台:

Visual C++

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // File: PeakMeterCtrl.cpp
  3. // Version: 1.2
  4. //
  5. // Author: Ernest Laurentin
  6. // E-mail: elaurentin@sympatico.ca
  7. //
  8. // Implementation of the CPeakMeterCtrl and associated classes.
  9. //
  10. // This code may be used in compiled form in any way you desire. This
  11. // file may be redistributed unmodified by any means PROVIDING it is
  12. // not sold for profit without the authors written consent, and
  13. // providing that this notice and the authors name and all copyright
  14. // notices remains intact.
  15. //
  16. // An email letting me know how you are using it would be nice as well.
  17. //
  18. // This file is provided "as is" with no expressed or implied warranty.
  19. // The author accepts no liability for any damage/loss of business that
  20. // this c++ class may cause.
  21. //
  22. // Version history
  23. //
  24. // 1.0 - Initial release.
  25. // 1.1 - Impoved Falloff effect to look more like Audio system
  26. //  1.2 - Added 3 ranges of color (low, medium, high)
  27. ///////////////////////////////////////////////////////////////////////////////
  28. #include "stdafx.h"
  29. #include <mmsystem.h>
  30. #include "MemDC.h"
  31. #include "PeakMeterCtrl.h"
  32. #pragma comment(lib, "winmm.lib")
  33. #ifdef _DEBUG
  34. #define new DEBUG_NEW
  35. #undef THIS_FILE
  36. static char THIS_FILE[] = __FILE__;
  37. #endif
  38. template <class T>
  39. bool InRange(const T val, const T valMin, const T valMax)
  40. {
  41. return ( val >= valMin && val <= valMax );
  42. }
  43. COLORREF LightenColor(const COLORREF crColor, BYTE byIncreaseVal)
  44. {
  45. BYTE byRed = GetRValue(crColor);
  46. BYTE byGreen = GetGValue(crColor);
  47. BYTE byBlue = GetBValue(crColor);
  48. byRed = (byRed + byIncreaseVal) <= 255 ? BYTE(byRed + byIncreaseVal) : 255;;
  49. byGreen = (byGreen + byIncreaseVal) <= 255 ? BYTE(byGreen + byIncreaseVal) : 255;
  50. byBlue = (byBlue + byIncreaseVal) <= 255 ? BYTE(byBlue + byIncreaseVal) : 255;
  51. return RGB(byRed, byGreen, byBlue);
  52. }
  53. COLORREF DarkenColor(const COLORREF crColor, BYTE byReduceVal)
  54. {
  55. BYTE byRed = GetRValue(crColor);
  56. BYTE byGreen = GetGValue(crColor);
  57. BYTE byBlue = GetBValue(crColor);
  58. byRed = byRed >= byReduceVal ? BYTE(byRed - byReduceVal) : 0;
  59. byGreen = byGreen >= byReduceVal ? BYTE(byGreen - byReduceVal) : 0;
  60. byBlue = (byBlue >= byReduceVal) ? BYTE(byBlue - byReduceVal) : 0;
  61. return RGB(byRed, byGreen, byBlue);
  62. }
  63. ///////////////////////////////////////////////////////////////////////////////
  64. // PeakMeterData class members
  65. PeakMeterData& PeakMeterData::Copy(const PeakMeterData& pm)
  66. {
  67. nValue = pm.nValue;
  68. nFalloff = pm.nFalloff;
  69. nPeak = pm.nPeak;
  70. return *this;
  71. }
  72. bool PeakMeterData::IsEqual(const PeakMeterData& pm)
  73. {
  74. return (nValue == pm.nValue);
  75. }
  76. bool PeakMeterData::IsGreater(const PeakMeterData& pm)
  77. {
  78. return ( nValue > pm.nValue );
  79. }
  80. bool PeakMeterData::IsLower(const PeakMeterData& pm)
  81. {
  82. return ( nValue < pm.nValue );
  83. }
  84. /////////////////////////////////////////////////////////////////////////////
  85. // CPeakMeterCtrl
  86. CPeakMeterCtrl::CPeakMeterCtrl()
  87. {
  88. TIMECAPS timecaps = {0};
  89. MMRESULT mmresult = timeGetDevCaps(&timecaps, sizeof(timecaps));
  90. mmresult = timeBeginPeriod( timecaps.wPeriodMin );
  91. // Initializes all data
  92. InitData();
  93. }
  94. CPeakMeterCtrl::~CPeakMeterCtrl()
  95. {
  96. TIMECAPS timecaps = {0};
  97. MMRESULT mmresult = timeGetDevCaps(&timecaps, sizeof(timecaps));
  98. mmresult = timeEndPeriod( timecaps.wPeriodMin );
  99. }
  100. BEGIN_MESSAGE_MAP(CPeakMeterCtrl, CWnd)
  101. //{{AFX_MSG_MAP(CPeakMeterCtrl)
  102. ON_WM_CREATE()
  103. ON_WM_DESTROY()
  104. ON_WM_PAINT()
  105. //}}AFX_MSG_MAP
  106. END_MESSAGE_MAP()
  107. ///////////////////////////////////////////////////////////////////////////////
  108. // Create
  109. BOOL CPeakMeterCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
  110. {
  111. // TODO: Add your specialized code here and/or call the base class
  112. dwStyle |= WS_CHILD;
  113. return CWnd::Create(NULL, NULL, dwStyle, rect, pParentWnd, nID, pContext);
  114. }
  115. ///////////////////////////////////////////////////////////////////////////////
  116. // PreSubclassWindow
  117. void CPeakMeterCtrl::PreSubclassWindow() 
  118. {
  119. ResetControl();
  120. CWnd::PreSubclassWindow();
  121. }
  122. ///////////////////////////////////////////////////////////////////////////////
  123. // InitData
  124. void CPeakMeterCtrl::InitData()
  125. {
  126. const COLORREF colLime = RGB(  0,255,  0);
  127. const COLORREF colRed  = RGB(255,  0,  0);
  128. const COLORREF colYellow  = RGB(255, 255,  0);
  129. m_bShowGrid = false;
  130. m_bShowFalloff = true;
  131. m_uTimerID = 0; // no timer
  132. m_nDelay = DELAY_10MS;
  133. m_nMinValue = 60; // Min Range 0-60
  134. m_nMedValue = 80; // Med Range 60-80
  135. m_nMaxValue = 100; // Max Range 80-100
  136. m_nNumBands = BAND_DEFAULT;
  137. m_nLedBands = LEDS_DEFAULT;
  138. m_clrBackground = GetSysColor(COLOR_3DFACE);
  139. m_clrNormal = colLime;
  140. m_clrMedium = colYellow;
  141. m_clrHigh = colRed;
  142. m_nSpeed = DEFAULT_SPEED;
  143. // clear vector data
  144. m_MeterData.clear();
  145. }
  146. ///////////////////////////////////////////////////////////////////////////////
  147. // ResetControl
  148. void CPeakMeterCtrl::ResetControl()
  149. {
  150. // Initialize vector
  151. m_MeterData.resize( m_nNumBands);
  152. // default data
  153. PeakMeterData pm;
  154. pm.nValue = m_nMaxValue;
  155. pm.nFalloff = m_nMaxValue;
  156. pm.nPeak  = m_nSpeed;
  157. for(int i=0; i < m_MeterData.size(); i++)
  158. {
  159. m_MeterData.at(i) = pm;
  160. }
  161. }
  162. ///////////////////////////////////////////////////////////////////////////////
  163. // SetBackgroundColor
  164. // DESCRIPTION: Set background color for this control
  165. void CPeakMeterCtrl::SetBackgroundColor(COLORREF colorBgnd)
  166. {
  167. m_clrBackground = colorBgnd;
  168. }
  169. ///////////////////////////////////////////////////////////////////////////////
  170. // SetBandsColor
  171. // DESCRIPTION: Set bands color for this control
  172. void CPeakMeterCtrl::SetBandsColor(COLORREF colorNormal, COLORREF colorMedium, COLORREF colorHigh)
  173. {
  174. m_clrNormal = colorNormal;
  175. m_clrMedium = colorMedium;
  176. m_clrHigh   = colorHigh;
  177. }
  178. ///////////////////////////////////////////////////////////////////////////////
  179. // SetMeterBands
  180. // DESCRIPTION: Set number of Vertical or Horizontal bands to display
  181. // NOTES:       Obtain smooth effect by setting nHorz or nVert to "1", these
  182. //              cannot be 0
  183. void CPeakMeterCtrl::SetMeterBands(int nNumBands, int nLedBands)
  184. {
  185. ASSERT( nNumBands>0 && nLedBands>0 );
  186. if (nNumBands == 0 || nLedBands == 0)
  187. return;
  188. m_nNumBands = nNumBands;
  189. m_nLedBands = nLedBands;
  190. // Reset vector
  191. ResetControl();
  192. }
  193. ///////////////////////////////////////////////////////////////////////////////
  194. // SetRangeValue
  195. // Min: [0 - nMin[,  Med: [nMin - nMed[,  Max: [nMed - nMax]
  196. void CPeakMeterCtrl::SetRangeValue(int nMin, int nMed, int nMax)
  197. {
  198. ASSERT( nMax > nMed && nMed > nMin && nMin > 0);
  199. m_nMinValue = nMin;
  200. m_nMedValue = nMed;
  201. m_nMaxValue = nMax;
  202. }
  203. ///////////////////////////////////////////////////////////////////////////////
  204. // GetRangeValue
  205. // DESCRIPTION: Get Range value of this control
  206. void CPeakMeterCtrl::GetRangeValue(int* lpiMin, int* lpiMed, int* lpiMax) const
  207. {
  208. if (NULL != lpiMin)
  209. *lpiMin = m_nMinValue;
  210. if (NULL != lpiMed)
  211. *lpiMed = m_nMedValue;
  212. if (NULL != lpiMax)
  213. *lpiMax = m_nMaxValue;
  214. }
  215. ///////////////////////////////////////////////////////////////////////////////
  216. // SetFalloffDelay
  217. // DESCRIPTION: Set Peak value speed before falling off
  218. void CPeakMeterCtrl::SetFalloffDelay(int nSpeed)
  219. {
  220. m_nSpeed = nSpeed;
  221. }
  222. ///////////////////////////////////////////////////////////////////////////////
  223. // SetFalloffEffect
  224. // DESCRIPTION: Set falloff effect flag
  225. void CPeakMeterCtrl::SetFalloffEffect(bool bFalloffEffect)
  226. {
  227. if (m_bShowFalloff != bFalloffEffect)
  228. {
  229. m_bShowFalloff = bFalloffEffect;
  230. Refresh();
  231. }
  232. }
  233. ///////////////////////////////////////////////////////////////////////////////
  234. // GetFalloffEffect
  235. // DESCRIPTION: Read falloff effect flag
  236. bool CPeakMeterCtrl::GetFalloffEffect() const
  237. {
  238. return m_bShowFalloff;
  239. }
  240. ///////////////////////////////////////////////////////////////////////////////
  241. // ShowGrid
  242. // DESCRIPTION: Request to have gridlines visible or not
  243. void CPeakMeterCtrl::ShowGrid(bool bShowGrid)
  244. {
  245. if (m_bShowGrid != bShowGrid)
  246. {
  247. m_bShowGrid = bShowGrid;
  248. Refresh();
  249. }
  250. }
  251. ///////////////////////////////////////////////////////////////////////////////
  252. // IsGridVisible
  253. // DESCRIPTION: Returns if gridlines are visible
  254. bool CPeakMeterCtrl::IsGridVisible() const
  255. {
  256. return m_bShowGrid;
  257. }
  258. ///////////////////////////////////////////////////////////////////////////////
  259. // SetData
  260. // DESCRIPTION: Change data value.  Use this function to change only
  261. //              a set of values.  All bands can be changed or only 1 band.
  262. //              Depending on the application. 
  263. bool CPeakMeterCtrl::SetData(const int ArrayValue[], int nOffset, int nSize)
  264. {
  265. ASSERT( nOffset >= 0 && ArrayValue != NULL);
  266. if (nOffset < 0 || ArrayValue == NULL)
  267. return false;
  268. bool bIsRunning = IsStarted();
  269. // Stop timer if Animation is active
  270. if ( bIsRunning )
  271. Stop();
  272. int nMaxSize = nOffset + nSize;
  273. for(int i=nOffset; i < nMaxSize; i++)
  274. {
  275. if (i < m_MeterData.size())
  276. {
  277. PeakMeterData pm = m_MeterData.at( i );
  278. pm.nValue = ArrayValue[i];
  279. if (pm.nFalloff < pm.nValue)
  280. {
  281. pm.nFalloff = pm.nValue;
  282. pm.nPeak    = m_nSpeed;
  283. }
  284. m_MeterData.at(i) = pm;
  285. }
  286. }
  287. // Auto-restart
  288. if ( bIsRunning )
  289. return Start( m_nDelay );
  290. Refresh();
  291. return true;
  292. }
  293. ///////////////////////////////////////////////////////////////////////////////
  294. // Refresh
  295. // DESCRIPTION: Invalidate the control
  296. void CPeakMeterCtrl::Refresh()
  297. {
  298. if (::IsWindow( GetSafeHwnd() ))
  299. Invalidate();
  300. }
  301. ///////////////////////////////////////////////////////////////////////////////
  302. // IsStarted
  303. // DESCRIPTION: Check if animation is active
  304. bool CPeakMeterCtrl::IsStarted() const
  305. {
  306. return (m_uTimerID > 0);
  307. }
  308. ///////////////////////////////////////////////////////////////////////////////
  309. // Start
  310. // DESCRIPTION: Start the timer and animation effect
  311. bool CPeakMeterCtrl::Start(UINT uDelay)
  312. {
  313. if ( !IsStarted() )
  314. {
  315. MMRESULT mmresult;
  316. mmresult = timeSetEvent(uDelay, 0, PeakTimerProc, (DWORD)this, TIME_PERIODIC);
  317. m_uTimerID = (UINT) mmresult;
  318. m_nDelay   = uDelay;
  319. }
  320. return (m_uTimerID != 0);
  321. }
  322. ///////////////////////////////////////////////////////////////////////////////
  323. // Stop
  324. // DESCRIPTION: Stop the timer and animation effect
  325. bool CPeakMeterCtrl::Stop()
  326. {
  327. if ( IsStarted() )
  328. {
  329. timeKillEvent( m_uTimerID );
  330. m_uTimerID = 0;
  331. return true;
  332. }
  333. return false;
  334. }
  335. ///////////////////////////////////////////////////////////////////////////////
  336. // DoTimerProcessing
  337. // DESCRIPTION: Peak Meter animation - virtual
  338. void CPeakMeterCtrl::DoTimerProcessing(UINT uID)
  339. {
  340. Refresh();
  341. int nDecValue  = m_nMaxValue / m_nLedBands;
  342. bool bNoChange = true;
  343. for(int i=0; i < m_MeterData.size(); i++)
  344. {
  345. PeakMeterData pm = m_MeterData.at(i);
  346. if (pm.nValue > 0)
  347. {
  348. pm.nValue -= ( m_nLedBands>1 ? nDecValue : m_nMaxValue*BAND_PERCENT/100);
  349. if (pm.nValue < 0)
  350. pm.nValue = 0;
  351. bNoChange = false;
  352. }
  353. if (pm.nPeak > 0)
  354. {
  355. pm.nPeak -= 1;
  356. bNoChange = false;
  357. }
  358. if (pm.nPeak == 0 && pm.nFalloff > 0)
  359. {
  360. pm.nFalloff -= (m_nLedBands>1 ? nDecValue>>1 : 5);
  361. if (pm.nFalloff < 0)
  362. pm.nFalloff = 0;
  363. bNoChange = false;
  364. }
  365. // re-assign PeakMeterData
  366. m_MeterData.at(i) = pm;
  367. }
  368. if (bNoChange) // Stop timer if no more data but do not reset ID
  369. {
  370. timeKillEvent( m_uTimerID );
  371. }
  372. }
  373. ///////////////////////////////////////////////////////////////////////////////
  374. // CPeakMeterCtrl message handlers
  375. ///////////////////////////////////////////////////////////////////////////////
  376. // OnCreate
  377. int CPeakMeterCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  378. {
  379. if (CWnd::OnCreate(lpCreateStruct) == -1)
  380. return -1;
  381. //InitControl();
  382. return 0;
  383. }
  384. ///////////////////////////////////////////////////////////////////////////////
  385. // OnDestroy
  386. void CPeakMeterCtrl::OnDestroy() 
  387. {
  388. Stop();
  389. // clear vector data
  390. m_MeterData.clear();
  391. CWnd::OnDestroy();
  392. }
  393. ///////////////////////////////////////////////////////////////////////////////
  394. // OnPaint
  395. // DESCRIPTION: Paint this control
  396. void CPeakMeterCtrl::OnPaint() 
  397. {
  398. CPaintDC dc(this); // device context for painting
  399. CRect rc;
  400. GetWindowRect( rc );
  401. rc.OffsetRect(-rc.left, -rc.top);
  402. CMemDC memdc(&dc, &rc);
  403. memdc.SetBkMode(TRANSPARENT);
  404. memdc.FillSolidRect( rc, m_clrBackground);
  405. CPen   pen;
  406. //pen.CreatePen(PS_SOLID, 1, RGB(255,0,0));
  407. pen.CreatePen(PS_SOLID, 1, DarkenColor(m_clrBackground, GRID_INCREASEBY));
  408. CPen* pOldPen = (CPen*) memdc.SelectObject( & pen );
  409. if (GetStyle() & PMS_VERTICAL)
  410. DrawVertBand(&memdc, rc);
  411. else
  412. DrawHorzBand(&memdc, rc);
  413. memdc.SelectObject( pOldPen );
  414. // Do not call CWnd::OnPaint() for painting messages
  415. }
  416. ///////////////////////////////////////////////////////////////////////////////
  417. // DrawVertBand
  418. // DESCRIPTION: Draw Vertical bands - No falloff effect for vertical bands
  419. void CPeakMeterCtrl::DrawVertBand(CDC* pDC, CRect& rcClient)
  420. {
  421. int nHorzBands = (m_nLedBands>1 ? m_nLedBands : m_nMaxValue*BAND_PERCENT/100);
  422. int nMinHorzLimit = m_nMinValue*nHorzBands/m_nMaxValue;
  423. int nMedHorzLimit = m_nMedValue*nHorzBands/m_nMaxValue;
  424. int nMaxHorzLimit = nHorzBands;
  425. CSize size;
  426. size.cx = rcClient.Width()/nHorzBands;
  427. size.cy = rcClient.Height()/m_nNumBands;
  428. CRect rcBand( rcClient.TopLeft(), size);
  429. // Draw band from top
  430. rcBand.OffsetRect(0, rcClient.Height()-size.cy*m_nNumBands);
  431. int xDecal = (m_nLedBands>1 ? 1 : 0);
  432. int yDecal = (m_nNumBands>1 ? 1 : 0);
  433. for(int nVert=0; nVert < m_nNumBands; nVert++)
  434. {
  435. int nValue = m_MeterData.at(nVert).nValue;
  436. int nHorzLimit = nValue*nHorzBands/m_nMaxValue;
  437. for(int nHorz=0; nHorz < nHorzBands; nHorz++)
  438. {
  439. rcBand.DeflateRect(xDecal, yDecal, 0, yDecal);
  440. // Find color based on range value
  441. COLORREF colorRect = m_clrBackground;
  442. if (true == m_bShowGrid)
  443. colorRect = DarkenColor(m_clrBackground, GRID_INCREASEBY);
  444. if ( m_bShowGrid && (nHorz == nMinHorzLimit || nHorz == (nHorzBands-1)))
  445. {
  446. POINT points[2] = { 0 };
  447. points[0].x = rcBand.TopLeft().x + (rcBand.Width()>>1);
  448. points[0].y = rcBand.TopLeft().y - yDecal;
  449. points[1].x = points[0].x;
  450. points[1].y = rcBand.BottomRight().y + yDecal;
  451. pDC->Polyline( points, 2);
  452. }
  453. if ( nHorz < nHorzLimit)
  454. {
  455. if ( InRange<int>(nHorz, 0, nMinHorzLimit-1) )
  456. colorRect = m_clrNormal;
  457. else if ( InRange<int>(nHorz, nMinHorzLimit, nMedHorzLimit-1) )
  458. colorRect = m_clrMedium;
  459. else if ( InRange<int>(nHorz, nMedHorzLimit, nMaxHorzLimit) )
  460. colorRect = m_clrHigh;
  461. }
  462. pDC->FillSolidRect( rcBand, colorRect);
  463. rcBand.InflateRect(xDecal, yDecal, 0, yDecal);
  464. rcBand.OffsetRect(size.cx, 0);
  465. }
  466. // Move to Next Vertical band
  467. rcBand.OffsetRect(-size.cx*nHorzBands, size.cy);
  468. }
  469. }
  470. ///////////////////////////////////////////////////////////////////////////////
  471. // DrawHorzBand
  472. // DESCRIPTION: Draw Horizontal bands - with Falloff effect
  473. void CPeakMeterCtrl::DrawHorzBand(CDC* pDC, CRect& rcClient)
  474. {
  475. int nVertBands = (m_nLedBands>1 ? m_nLedBands : m_nMaxValue*BAND_PERCENT/100);
  476. int nMinVertLimit = m_nMinValue*nVertBands/m_nMaxValue;
  477. int nMedVertLimit = m_nMedValue*nVertBands/m_nMaxValue;
  478. int nMaxVertLimit = nVertBands;
  479. CSize size;
  480. size.cx = rcClient.Width()/m_nNumBands;
  481. size.cy = rcClient.Height()/nVertBands;
  482. CRect rcBand( rcClient.TopLeft(), size);
  483. // Draw band from bottom
  484. rcBand.OffsetRect(0, rcClient.bottom-size.cy);
  485. int xDecal = (m_nNumBands>1 ? 1 : 0);
  486. int yDecal = (m_nLedBands>1 ? 1 : 0);
  487. for(int nHorz=0; nHorz < m_nNumBands; nHorz++)
  488. {
  489. int nValue = m_MeterData.at(nHorz).nValue;
  490. int nVertLimit = nValue*nVertBands/m_nMaxValue;
  491. CRect rcPrev = rcBand;
  492. for(int nVert=0; nVert < nVertBands; nVert++)
  493. {
  494. rcBand.DeflateRect(xDecal, yDecal, xDecal, 0);
  495. // Find color based on range value
  496. COLORREF colorRect = m_clrBackground;
  497. if ( true == m_bShowGrid)
  498. colorRect = DarkenColor(m_clrBackground, GRID_INCREASEBY);
  499. // Draw grid line (level) bar
  500. if ( m_bShowGrid && (nVert == nMinVertLimit || nVert == (nVertBands-1)))
  501. {
  502. POINT points[2] = { 0 };
  503. points[0].x = rcBand.TopLeft().x - xDecal;
  504. points[0].y = rcBand.TopLeft().y + (rcBand.Height()>>1);
  505. points[1].x = rcBand.BottomRight().x + xDecal;
  506. points[1].y = points[0].y;
  507. pDC->Polyline( points, 2);
  508. }
  509. if ( nVert < nVertLimit)
  510. {
  511. if ( InRange<int>(nVert, 0, nMinVertLimit-1) )
  512. colorRect = m_clrNormal;
  513. else if ( InRange<int>(nVert, nMinVertLimit, nMedVertLimit-1) )
  514. colorRect = m_clrMedium;
  515. else if ( InRange<int>(nVert, nMedVertLimit, nMaxVertLimit) )
  516. colorRect = m_clrHigh;
  517. }
  518. pDC->FillSolidRect( rcBand, colorRect);
  519. rcBand.InflateRect(xDecal, yDecal, xDecal, 0);
  520. rcBand.OffsetRect(0, -size.cy);
  521. }
  522. // Draw falloff effect
  523. if (m_bShowFalloff)
  524. {
  525. CPen pen;
  526. pen.CreatePen(PS_SOLID, 1, DarkenColor(m_clrBackground, FALL_INCREASEBY));
  527. CPen* pOldPen = (CPen*) pDC->SelectObject(& pen );
  528. int nMaxHeight = size.cy*nVertBands;
  529. POINT points[2] = { 0 };
  530. points[0].x = rcPrev.TopLeft().x + xDecal;
  531. points[0].y = rcPrev.BottomRight().y - m_MeterData.at(nHorz).nFalloff*nMaxHeight/m_nMaxValue;
  532. points[1].x = rcPrev.BottomRight().x - xDecal;
  533. points[1].y = points[0].y;
  534. pDC->Polyline( points, 2);
  535. pDC->SelectObject( pOldPen );
  536. }
  537. // Move to Next Horizontal band
  538. rcBand.OffsetRect(size.cx, size.cy*nVertBands);
  539. }
  540. }
  541. ///////////////////////////////////////////////////////////////////////////////
  542. // PeakTimerProc
  543. // DESCRIPTION: Multimedia timer procedure - this is called from another thread
  544. void CALLBACK CPeakMeterCtrl::PeakTimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
  545. {
  546. CPeakMeterCtrl* pThis = reinterpret_cast<CPeakMeterCtrl*>( dwUser );
  547. ASSERT( pThis != NULL );
  548. pThis->DoTimerProcessing( uID );
  549. }