PianoCtrl.cpp
上传用户:fs3633
上传日期:2021-05-14
资源大小:909k
文件大小:20k
源码类别:

midi

开发平台:

Visual C++

  1. /*
  2.   CPianoCtrl.cpp
  3.   This file represents the implementation for the CPianoCtrl class
  4.   Copyright (C) 2002 Leslie Sanford
  5.   This library is free software; you can redistribute it and/or
  6.   modify it under the terms of the GNU Lesser General Public
  7.   License as published by the Free Software Foundation; either
  8.   version 2.1 of the License, or (at your option) any later version.
  9.   This library is distributed in the hope that it will be useful,
  10.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12.   Lesser General Public License for more details.
  13.   You should have received a copy of the GNU Lesser General Public
  14.   License along with this library; if not, write to the Free Software
  15.   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 
  16.   USA
  17.   Contact: Leslie Sanford (jabberdabber@hotmail.com)
  18.   Last modified: 12/18/2002
  19. */
  20. //---------------------------------------------------------------------
  21. // Dependencies
  22. //---------------------------------------------------------------------
  23. #include <afxwin.h>  
  24. #include "PianoCtrl.h"
  25. #ifdef _DEBUG
  26. #define new DEBUG_NEW
  27. #undef THIS_FILE
  28. static char THIS_FILE[] = __FILE__;
  29. #endif
  30. /////////////////////////////////////////////////////////////////////////////
  31. // CPianoCtrl
  32. //---------------------------------------------------------------------
  33. // Constants
  34. //---------------------------------------------------------------------
  35. // Class name for this control 設置注冊類的名字
  36. const char CPianoCtrl::CLASS_NAME[] = _T("PianoCtrl");
  37. // Default note-on color 默認的音符顏色
  38. const COLORREF CPianoCtrl::DEF_NOTE_ON_COLOR = RGB(0, 150, 250);
  39. // Maximum number of notes 音符的最大數值
  40. const int CPianoCtrl::MAX_NOTE_COUNT = 128;
  41. // Note table //音符表
  42. const int CPianoCtrl::NOTE_TABLE[] = 
  43.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  44.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  45.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  46.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  47.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  48.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  49.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  50.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  51.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  52.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B,
  53.     C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G
  54. };
  55. // Range error messages//错误消息列表
  56. const CString CPianoCtrl::NOTE_RANGE_ERR = _T("Note out of range.");
  57. const CString CPianoCtrl::LOW_NOTE_RANGE_ERR = _T("Low note out of range.");
  58. const CString CPianoCtrl::HIGH_NOTE_RANGE_ERR = _T("High note out of range.");
  59. const CString CPianoCtrl::INVALID_RANGE_ERR = _T("Note range is invalid.");
  60. const CString CPianoCtrl::NATURAL_NOTE_ERR = _T("Notes must be natural.");
  61. // Flags whether or not this custom control class has been registered.
  62. bool CPianoCtrl::REGISTER_FLAG = false;
  63.   
  64. //---------------------------------------------------------------------
  65. // CPianoCtrl class implementation
  66. //---------------------------------------------------------------------
  67. // Constructor
  68. CPianoCtrl::CPianoCtrl() : 
  69. m_Keys(0),
  70. m_LowNote(0),
  71. m_HighNote(0),
  72. m_CurrKey(-1),
  73. m_IsInitialized(false),
  74. m_HasCapture(false)
  75. {
  76.     // If this control has not been registered, do it here.
  77.     //如果這個控件已經注冊成功了,那么就在這裡執行
  78.     if(!REGISTER_FLAG)
  79.     {
  80.         RegisterWindowClass();
  81.     }
  82.     // Initialize critical section
  83.     //初始化臨界面
  84.     ::InitializeCriticalSection(&m_CriticalSection);
  85. }
  86. // Destructor
  87. CPianoCtrl::~CPianoCtrl()
  88. {
  89.     DestroyPianoKeys();
  90.     ::DeleteCriticalSection(&m_CriticalSection);
  91. }
  92. // Create function for creating the control dynamically
  93. //創建控件的函數
  94. BOOL CPianoCtrl::Create(CWnd *pParentWnd, const RECT &rect, UINT nId, 
  95.                         DWORD dwStyle)
  96. {
  97.     return CWnd::Create(CLASS_NAME, _T(""), dwStyle, rect, pParentWnd, 
  98.                         nId);
  99. }
  100. // Initializes the control 初始化控件
  101. BOOL CPianoCtrl::Initialize(unsigned char LowNote, 
  102.                             unsigned char HighNote,
  103.                             COLORREF NoteOnColor)
  104. {
  105.     CRect ClientRect;
  106.     
  107.     // Get the rectangular coordinates of this control and get its 
  108.     // width and length
  109.     //得到這個控件的坐標範圍,並且得到它的寬和長
  110.     GetClientRect(&ClientRect);
  111.     m_Width = ClientRect.bottom;
  112.     m_Length = ClientRect.right - 1;
  113.     // Initialize note-on color 初始化音符的顏色
  114.     m_NoteOnColor = NoteOnColor;
  115.     // If we successfully set the note range, we are finished 
  116.     // initializing this control. Indicate that it has been initialized.
  117.     //如果我們成功的設置了音符的排列,我們就算完成了。
  118.     //初始化這個控件,並且顯示出它已經被初始化了
  119.     if(SetNoteRange(LowNote, HighNote))
  120.     {
  121.         m_IsInitialized = true;
  122.     }
  123.     return TRUE ? m_IsInitialized : FALSE;
  124. }
  125. // Turn on a note //开启一个音符
  126. void CPianoCtrl::NoteOn(unsigned char NoteId, COLORREF NoteOnColor)
  127. {
  128.     // Make sure this control has been initialized//确定这个控件有没有初始化
  129.     if(m_IsInitialized)
  130.     {
  131.         // Range checking//开始检查
  132.         if(NoteId < m_LowNote || NoteId > m_HighNote)
  133.         {
  134.             MessageBox(NOTE_RANGE_ERR, "Error", MB_OK | MB_ICONSTOP);
  135.         }
  136.         // Turn on note and invalidate its area so that it is repainted.
  137.         else
  138.         {
  139.             CRect InvalidRect;
  140.             m_Keys[NoteId - m_LowNote]->NoteOn(NoteOnColor, InvalidRect);
  141.             NotifyNoteOn(NoteId);
  142.             InvalidateRect(InvalidRect);
  143.         }
  144.     }
  145.     
  146.     
  147. }
  148. // Turn on note 打开音符
  149. void CPianoCtrl::NoteOn(unsigned char NoteId)
  150. {
  151.     NoteOn(NoteId, m_NoteOnColor);
  152. }
  153. // Turn off note
  154. void CPianoCtrl::NoteOff(unsigned char NoteId)
  155. {
  156.     // Make sure this control has been initialized 这个控件必须初始化
  157.     if(m_IsInitialized)
  158.     {
  159.         // Range checking 开始检查
  160.         if(NoteId < m_LowNote || NoteId > m_HighNote)
  161.         {
  162.             MessageBox(NOTE_RANGE_ERR, "Error", MB_OK | MB_ICONSTOP);
  163.         }
  164.         // Turn note off and invalidate its area so that it can be 
  165.         // repainted 开启音符或者使它无效,只要在这个范围之内,它都能重新绘制
  166.         else
  167.         {
  168.             CRect InvalidRect;
  169.             m_Keys[NoteId - m_LowNote]->NoteOff(InvalidRect);
  170.             NotifyNoteOff(NoteId);
  171.             InvalidateRect(InvalidRect);
  172.         }
  173.     }
  174.     
  175. }
  176. // Attach listener for event notification 为事件通知连接上收听者
  177. void CPianoCtrl::AttachListener(CPianoCtrlListener &Listener)//如果沒有這個函數,就沒聲音了
  178. {
  179.     // Protect list with critical section
  180.     ::EnterCriticalSection(&m_CriticalSection);
  181.     m_Listeners.push_front(&Listener);
  182.     ::LeaveCriticalSection(&m_CriticalSection);
  183.     
  184. }
  185. // Detach listener 分离收听者
  186. void CPianoCtrl::DetachListener(CPianoCtrlListener &Listener)
  187. {
  188.     ::EnterCriticalSection(&m_CriticalSection);
  189.     m_Listeners.remove(&Listener);
  190.     ::LeaveCriticalSection(&m_CriticalSection);
  191. }
  192. // Set note range 放置音符队列
  193. BOOL CPianoCtrl::SetNoteRange(unsigned char LowNote, 
  194.                               unsigned char HighNote)
  195. {
  196.     BOOL Result = FALSE;
  197.     // Range checking 开始搜索
  198.     if(LowNote < 0)
  199.     {
  200.         MessageBox(LOW_NOTE_RANGE_ERR, "Error", MB_OK | MB_ICONSTOP);
  201.     }
  202.     // High note beyond maximum number allowed 高音符在最高的数字
  203.     else if(HighNote >= MAX_NOTE_COUNT)
  204.     {
  205.         MessageBox(HIGH_NOTE_RANGE_ERR, "Error", MB_OK | MB_ICONSTOP);
  206.     }
  207.     // Low note is higher than high note 否则低音符比高音符更高
  208.     else if(LowNote >= HighNote)
  209.     {
  210.         MessageBox(INVALID_RANGE_ERR, "Error", MB_OK | MB_ICONSTOP);
  211.     }
  212.     // Either the low note or the high note is not natural
  213.     //任意一个低音符或者高音符不是普通的音符
  214.     else if(!IsNoteNatural(LowNote) || !IsNoteNatural(HighNote))
  215.     {
  216.         MessageBox(NATURAL_NOTE_ERR, "Error", MB_OK | MB_ICONSTOP);
  217.     }
  218.     // Initialize range and create keys 初始化队列并且创建琴键
  219.     else
  220.     {
  221.         m_LowNote = LowNote;
  222.         m_HighNote = HighNote;
  223.         CreatePianoKeys();
  224.         // Indicate that the operation was successful 否则说明哪个操作是成功的,返回TRUE
  225.         Result = TRUE;
  226.     }
  227.     return Result;
  228.    
  229.     
  230. }
  231.     
  232. // Determines if a note is natural 确定这个音符是否是它对应的本位号
  233. bool CPianoCtrl::IsNoteNatural(unsigned char Note)
  234. {
  235.     bool Result = true;
  236.     if(NOTE_TABLE[Note] == C_SHARP || NOTE_TABLE[Note] == D_SHARP ||
  237.         NOTE_TABLE[Note] == F_SHARP || NOTE_TABLE[Note] == G_SHARP ||
  238.         NOTE_TABLE[Note] == A_SHARP)
  239.     {
  240.         Result = false;
  241.     }
  242.     
  243.     return Result;
  244. }
  245. // Get the number of natural notes in this control's range
  246. //在这个控件队列中得到音符对应的数字
  247. int CPianoCtrl::GetNaturalNoteCount()
  248. {
  249.     int NatNoteCount = 0;
  250.     for(unsigned char i = m_LowNote; i <= m_HighNote; i++)
  251.     {
  252.         if(IsNoteNatural(i))
  253.         {
  254.             NatNoteCount++;
  255.         }
  256.     }
  257.     return NatNoteCount;
  258.     
  259.     
  260. }
  261. // Calculate the unit length for this control 计算这个控件的单位长度
  262. double CPianoCtrl::GetUnitLength()
  263. {
  264.     // Determine the number of natural notes within this note range
  265.     int NatNoteCount = GetNaturalNoteCount();
  266.     double Result = static_cast<double>(m_Length) / NatNoteCount;
  267.     // Return the length of each unit 返回每个单位的长度
  268.     return Result / CPianoKey::UNIT_PER_NAT_KEY;
  269. }
  270. // Create the piano keys 创建琴键
  271. void CPianoCtrl::CreatePianoKeys()
  272. {
  273.     double Position = 0;
  274.     DestroyPianoKeys();
  275.     m_Keys.resize(m_HighNote - m_LowNote + 1);
  276.     m_UnitLength = GetUnitLength();
  277.     //
  278.     // The leftmost key is a special case. Take care of it first.
  279.     // 最左边的琴键是特殊的,要首先考虑
  280.     if(NOTE_TABLE[m_LowNote] == B || NOTE_TABLE[m_LowNote] == E)
  281.     {
  282.         m_Keys[0] = new CWhiteKeyFull(m_UnitLength, m_Width, 0);
  283.         // Move position to the right 往右移动位置
  284.         Position = m_UnitLength * CPianoKey::UNIT_PER_NAT_KEY;
  285.     }
  286.     else
  287.     {
  288.         m_Keys[0] = new CWhiteKeyLeft(m_UnitLength, m_Width, 0);
  289.         // Move position to the right 继续右移
  290.         Position = m_UnitLength * (CPianoKey::UNIT_PER_NAT_KEY - 1);
  291.     }
  292.     //
  293.     // Create all but the rightmost key. 
  294.     //创建所有右边的琴键
  295.     for(int i = m_LowNote + 1; i < m_HighNote; i++)
  296.     {
  297.         // CWhiteKeyLeft cases
  298.         if(NOTE_TABLE[i] == C || NOTE_TABLE[i] == F)
  299.         {          
  300.             m_Keys[i - m_LowNote] = new CWhiteKeyLeft(m_UnitLength, 
  301.                 m_Width, Position);
  302.             // Move position to the right
  303.             Position += m_UnitLength * (CPianoKey::UNIT_PER_NAT_KEY - 1);
  304.         }
  305.         // CWhiteKeyMiddle cases
  306.         else if(NOTE_TABLE[i] == G || NOTE_TABLE[i] == A ||
  307.             NOTE_TABLE[i] == D)
  308.         {
  309.             m_Keys[i - m_LowNote] = new CWhiteKeyMiddle(m_UnitLength, 
  310.                 m_Width, Position);
  311.             // Move position to the right 移动到右边放置
  312.             Position += m_UnitLength * (CPianoKey::UNIT_PER_NAT_KEY - 1);
  313.         }
  314.         // CWhiteKeyRight cases
  315.         else if(NOTE_TABLE[i] == B || NOTE_TABLE[i] == E)
  316.         {
  317.             m_Keys[i - m_LowNote] = new CWhiteKeyRight(m_UnitLength, 
  318.                 m_Width, Position);
  319.             // Move position to the right
  320.             Position += m_UnitLength * (CPianoKey::UNIT_PER_NAT_KEY);
  321.         }
  322.         // CBlackKey cases 黑色琴键的设置
  323.         else
  324.         {
  325.             m_Keys[i - m_LowNote] = new CBlackKey(m_UnitLength, 
  326.                 m_Width, Position);
  327.             // Move position to the right
  328.             Position += m_UnitLength;
  329.         }
  330.     }
  331.     //
  332.     // The rightmost key is a special case. Take care of it last.
  333.     //最右面的琴键是特殊的,最后在考虑
  334.     if(NOTE_TABLE[m_HighNote] == C || 
  335.         NOTE_TABLE[m_HighNote] == F)
  336.     {
  337.         m_Keys[m_HighNote - m_LowNote] = new 
  338.             CWhiteKeyFull(m_UnitLength, m_Width, Position);
  339.     }
  340.     else
  341.     {
  342.         m_Keys[m_HighNote - m_LowNote] = new 
  343.             CWhiteKeyRight(m_UnitLength, m_Width, Position);
  344.     }
  345.     // Paint new keyboard 绘制新的键盘
  346.     Invalidate();
  347.     
  348. }
  349. // Destroy the piano keys 销毁这些琴键
  350. void CPianoCtrl::DestroyPianoKeys()
  351. {
  352.     // Make sure there are keys to destroy 必须销毁一些琴键
  353.     if(m_Keys.size() > 0)
  354.     {
  355.         for(int i = 0; i < m_Keys.size(); i++)
  356.         {
  357.             delete m_Keys[i];
  358.         }
  359.     }
  360.     // Resise the container to 0 indicating that there are no keys in 
  361.     // the control at this time
  362.     m_Keys.resize(0);
  363. }
  364. // Find a key in this control 在这个控件上面寻找一个琴键
  365. int CPianoCtrl::FindKey(CPoint &point)
  366. {
  367.     bool FoundFlag = false;
  368.     int Key = 0;
  369.     // This algorithm does a simple linear search for the key that was
  370.     // pressed. This is not an efficient way to search. However, it's
  371.     // the simplest and works well enough in practise for this situation.
  372.     //這個算法是一個簡單的線性搜索某個琴鍵是不是按下去了,這不是一個有效的
  373.     //搜尋方法
  374.     while(!FoundFlag && Key < m_Keys.size() - 1)
  375.     {
  376.         if(m_Keys[Key]->IsPointInKey(point))
  377.         {
  378.             FoundFlag = true;
  379.         }
  380.         else
  381.         {
  382.             Key++;
  383.         }
  384.     }
  385.     return Key;
  386.     
  387.     
  388. }
  389. // Notify all listeners that a note-on event has occurred
  390. //通报所有的收听者有一个音符开启的事件发生了
  391. void CPianoCtrl::NotifyNoteOn(unsigned char NoteId)//如果沒有這個函數的話一樣沒有聲音
  392. {
  393.     // Protect list with critical section
  394.     ::EnterCriticalSection(&m_CriticalSection);
  395.     std::list<CPianoCtrlListener *>::iterator i;
  396.     for(i = m_Listeners.begin(); i != m_Listeners.end(); i++)
  397.     {
  398.         (*i)->OnNoteOn(*this, NoteId);
  399.     }
  400.     ::LeaveCriticalSection(&m_CriticalSection);
  401.     
  402. }
  403. // Notify listeners that a note-off event has occurred
  404. //通报所有的收听者有一个音符关闭的事件发生了
  405. void CPianoCtrl::NotifyNoteOff(unsigned char NoteId)//如果沒有這個函數的話就會有回音
  406. {
  407.     // Protect list with critical section
  408.     ::EnterCriticalSection(&m_CriticalSection);
  409.     std::list<CPianoCtrlListener *>::iterator i;
  410.     for(i = m_Listeners.begin(); i != m_Listeners.end(); i++)
  411.     {
  412.         (*i)->OnNoteOff(*this, NoteId);
  413.     }
  414.     ::LeaveCriticalSection(&m_CriticalSection);
  415.     
  416. }
  417. // Register this controls Window class
  418. //在Window窗口类里面注册一个控件
  419. void CPianoCtrl::RegisterWindowClass()
  420. {
  421.         
  422.     WNDCLASS WndClass;
  423.     HINSTANCE hInst = ::AfxGetInstanceHandle();
  424.     HCURSOR Cursor = ::AfxGetApp()->LoadStandardCursor(IDC_ARROW);
  425.     WndClass.style         = CS_HREDRAW | CS_VREDRAW;
  426.     WndClass.lpfnWndProc   = ::DefWindowProc;
  427.     WndClass.cbClsExtra    = WndClass.cbWndExtra = 0;
  428.     WndClass.hInstance     = hInst;
  429.     WndClass.hIcon         = NULL;
  430.     WndClass.hCursor       = Cursor;
  431.     WndClass.hbrBackground = NULL;
  432.     WndClass.lpszMenuName  = NULL;
  433.     WndClass.lpszClassName = CLASS_NAME;
  434.     
  435.    
  436.     if (::AfxRegisterClass(&WndClass))
  437.     {
  438.         // Indicate that this Window class has now been registered
  439.         //显示这个Windows类已经注册了
  440.         REGISTER_FLAG = true;
  441.     }
  442.     else
  443.     {
  444.         ::AfxThrowResourceException();
  445.     }
  446.     
  447.     
  448. }
  449. BEGIN_MESSAGE_MAP(CPianoCtrl, CWnd)
  450. //{{AFX_MSG_MAP(CPianoCtrl)
  451. ON_WM_PAINT()
  452. ON_WM_LBUTTONDOWN()
  453. ON_WM_LBUTTONUP()
  454. ON_WM_MOUSEMOVE()
  455. //}}AFX_MSG_MAP
  456. END_MESSAGE_MAP()
  457. /////////////////////////////////////////////////////////////////////////////
  458. // CPianoCtrl message handlers
  459. // Paints the control 绘制这个控件
  460. void CPianoCtrl::OnPaint() 
  461. {
  462. PAINTSTRUCT ps;
  463.     CDC *dc = BeginPaint(&ps);
  464.     for(int i = 0; i < m_Keys.size(); i++)
  465.     {
  466.         m_Keys[i]->Paint(dc);
  467.     }
  468.     EndPaint(&ps);
  469. }
  470. // On left mouse button down 鼠标把左边的BUTTON按钮按下
  471. void CPianoCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
  472. {
  473.     // Capture the mouse if we haven't already
  474.     //判断计算机的鼠标是不是已经准备好
  475.     if(!m_HasCapture)
  476.     {
  477.         SetCapture();
  478.         m_HasCapture = true;
  479.     }
  480.     // Make sure this control has been initialized
  481.     //必须先把这个控件初始化
  482.     if(m_IsInitialized)
  483.     {
  484.         // Find the key to turn on. This will become the current key. 
  485.         //寻找开启的琴键,这个将改变当前的琴键
  486.         // We keep track of it so that we can turn it off later
  487.         //我们必须明白这个,因为我们随后要打开它
  488.         m_CurrKey = FindKey(point);
  489.         // Turn on note, get its area, and then invalidate so that the
  490.         // control is repainted to indicate that this note is being 
  491.         // played 在这个区域开启一个音符,开启之后并且使之无效,因此这个控件必须重新绘制一
  492.         //一次,以确定这个音符能响动
  493.         CRect InvalidRect;
  494.        m_Keys[m_CurrKey]->NoteOn(m_NoteOnColor, InvalidRect);
  495.        NotifyNoteOn(m_LowNote+m_CurrKey);    
  496.         InvalidateRect(&InvalidRect);
  497.     }
  498. CWnd::OnLButtonDown(nFlags, point);
  499.     
  500. }
  501. // On left mouse button up 当鼠标松开左边这个BUTTON按钮的时候
  502. void CPianoCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
  503. {
  504.     // If there is a key already being played by the mouse, turn it off
  505.     //判断一个琴键是不是已经响动过一次,如果有的话必须关闭它
  506.     if(m_CurrKey >= 0)
  507.     {
  508.         CRect InvalidRect;
  509.         m_Keys[m_CurrKey]->NoteOff(InvalidRect);
  510.         NotifyNoteOff(m_CurrKey + m_LowNote);
  511.         // Indicate that there is no key active
  512.         //显示不活动的琴键
  513.         m_CurrKey = -1;
  514.         InvalidateRect(&InvalidRect);
  515.     }
  516. CWnd::OnLButtonUp(nFlags, point);
  517. // On mouse move 鼠标的移动
  518. void CPianoCtrl::OnMouseMove(UINT nFlags, CPoint point) 
  519. {
  520.     // If the mouse has moved outside of this window, turn off any 
  521.     // currently active key and release the mouse.
  522.     //如果鼠标移动到Windows的另一边,就要关闭所有活动的琴键,也要释放掉鼠标
  523.     if(point.x <= 0 || point.x >= m_Length || point.y <= 0 || 
  524.         point.y >= m_Width)
  525.     {   
  526.         // If there is a key currently active key
  527.         //判断当前活动着的琴键
  528.         if(m_CurrKey >= 0)
  529.         {
  530.             CRect InvalidRect;
  531.             m_Keys[m_CurrKey]->NoteOff(InvalidRect);
  532.             NotifyNoteOff(m_CurrKey + m_LowNote);
  533.             InvalidateRect(&InvalidRect);
  534.         }
  535.         ReleaseCapture();
  536.         m_HasCapture = false;
  537.     }
  538.     // If this piano control has been intialized, and it has captured the
  539.     // mouse, and the left mouse button is down...
  540.     //如果要初始化这个钢琴的控件,鼠标,还有鼠标按下左面的BUTTON按钮
  541.     if(m_IsInitialized && m_HasCapture && (nFlags & MK_LBUTTON))
  542.     {
  543.         // Find the key beneath the mouse cursor
  544.         //寻找在琴键之下的鼠标指针
  545.         int Key = FindKey(point); 
  546.         // If the mouse has moved to a new key
  547.         //判断鼠标有没有移动到新的琴键
  548.         if(Key != m_CurrKey)
  549.         {
  550.             CRect InvalidRect;
  551.             // If the current key is valid, turn note off
  552.             //判断当前的琴键是不是有效的,如果有就关闭它
  553.             if(m_CurrKey >= 0)
  554.             {               
  555.                 m_Keys[m_CurrKey]->NoteOff(InvalidRect);
  556.                 NotifyNoteOff(m_CurrKey + m_LowNote);
  557.                 InvalidateRect(&InvalidRect);
  558.             }
  559.             // Turn new note on 开启新的音符
  560.             m_CurrKey = Key;
  561.             m_Keys[m_CurrKey]->NoteOn(m_NoteOnColor, InvalidRect);
  562.             NotifyNoteOn(m_CurrKey + m_LowNote);
  563.             InvalidateRect(&InvalidRect);
  564.         }
  565.     }
  566.             
  567. CWnd::OnMouseMove(nFlags, point);
  568. }