using System; using System.Collections; using System.Drawing; using System.Threading; using System.Windows.Forms; using PIANO2.Message; namespace PIANO2.UI { public partial class PianoControl : Control { private enum KeyType { White, Black } private static readonly KeyType[] KeyTypeTable = { KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White, KeyType.Black, KeyType.White, KeyType.White, KeyType.Black, KeyType.White }; private const int DefaultLowNoteID = 21; private const int DefaultHighNoteID = 109; private const double BlackKeyScale = 0.666666666; private SynchronizationContext context; private int lowNoteID = DefaultLowNoteID; private int highNoteID = DefaultHighNoteID; private Color noteOnColor = Color.SkyBlue; private PianoKey[] keys = null; private int whiteKeyCount; private delegate void NoteMessageCallback(ChannelMessage message); private NoteMessageCallback noteOnCallback; private NoteMessageCallback noteOffCallback; public event EventHandler PianoKeyDown; public event EventHandler PianoMutedKeyDown; public event EventHandler PianoKeyUp; public PianoControl() { CreatePianoKeys(); InitializePianoKeys(); context = SynchronizationContext.Current; noteOnCallback = delegate(ChannelMessage message) { if(message.Data2 > 0) { keys[message.Data1 - lowNoteID].PressPianoKey(false); } else { keys[message.Data1 - lowNoteID].ReleasePianoKey(); } }; noteOffCallback = delegate(ChannelMessage message) { keys[message.Data1 - lowNoteID].ReleasePianoKey(); }; } private void CreatePianoKeys() { // If piano keys have already been created. if(keys != null) { // Remove and dispose of current piano keys. foreach(PianoKey key in keys) { Controls.Remove(key); key.Dispose(); } } keys = new PianoKey[HighNoteID - LowNoteID]; whiteKeyCount = 0; for(int i = 0; i < keys.Length; i++) { keys[i] = new PianoKey(this); keys[i].NoteID = i + LowNoteID; if(KeyTypeTable[keys[i].NoteID] == KeyType.White) { whiteKeyCount++; } else { keys[i].NoteOffColor = Color.Black; keys[i].BringToFront(); } keys[i].NoteOnColor = NoteOnColor; Controls.Add(keys[i]); } } private void InitializePianoKeys() { #region Guard if(keys.Length == 0) { return; } #endregion int whiteKeyWidth = Width / whiteKeyCount; int blackKeyWidth = (int)(whiteKeyWidth * BlackKeyScale); int blackKeyHeight = (int)(Height * BlackKeyScale); int offset = whiteKeyWidth - blackKeyWidth / 2; int n = 0; int w = 0; while(n < keys.Length) { if(KeyTypeTable[keys[n].NoteID] == KeyType.White) { keys[n].Height = Height; keys[n].Width = whiteKeyWidth; keys[n].Location = new Point(w * whiteKeyWidth, 0); keys[n].OctaveID = GetOctaveId(keys[n].NoteID); keys[n].NoteName = AssignName(keys[n].OctaveID); w++; n++; } else { keys[n].Height = blackKeyHeight; keys[n].Width = blackKeyWidth; keys[n].Location = new Point(offset + (w - 1) * whiteKeyWidth); keys[n].OctaveID = GetOctaveId(keys[n].NoteID); keys[n].NoteName = AssignName(keys[n].OctaveID); keys[n].BringToFront(); n++; } // Assign Name } } public int GetOctaveId(int NoteID) { NoteID = NoteID - lowNoteID; while (NoteID >= 12) { NoteID = NoteID - 12; } return NoteID; } public String AssignName(int OctaveId) { if (OctaveId == 0) { return "A"; } if (OctaveId == 1) { return "Bb"; } if (OctaveId == 2) { return "B"; } if (OctaveId == 3) { return "C"; } if (OctaveId == 4) { return "C#"; } if (OctaveId == 5) { return "D"; } if (OctaveId == 6) { return "Eb"; } if (OctaveId == 7) { return "E"; } if (OctaveId == 8) { return "F"; } if (OctaveId == 9) { return "F#"; } if (OctaveId == 10) { return "G"; } if (OctaveId == 11) { return "Ab"; } else { return "@"; } } public void PressPianoKey(int noteID, bool isMuted) { if(noteID < lowNoteID || noteID > highNoteID) { throw new ArgumentOutOfRangeException(); } keys[noteID - lowNoteID].PressPianoKey(isMuted); } public void ReleasePianoKey(int noteID) { if(noteID < lowNoteID || noteID > highNoteID) { throw new ArgumentOutOfRangeException(); } keys[noteID - lowNoteID].ReleasePianoKey(); } protected override void OnResize(EventArgs e) { InitializePianoKeys(); base.OnResize(e); } protected override void Dispose(bool disposing) { if(disposing) { foreach(PianoKey key in keys) { key.Dispose(); } } base.Dispose(disposing); } protected virtual void OnPianoKeyDown(PianoKeyEventArgs e) { EventHandler handler = PianoKeyDown; if(handler != null) { handler(this, e); } } protected virtual void OnMutedPianoKeyDown(PianoKeyEventArgs e) { EventHandler handler = PianoMutedKeyDown; if (handler != null) { handler(this, e); } } protected virtual void OnPianoKeyUp(PianoKeyEventArgs e) { EventHandler handler = PianoKeyUp; if(handler != null) { handler(this, e); } } public int LowNoteID { get { return lowNoteID; } set { #region Require if(value < 0 || value > ShortMessage.DataMaxValue) { throw new ArgumentOutOfRangeException("LowNoteID", value, "Low note ID out of range."); } #endregion #region Guard if(value == lowNoteID) { return; } #endregion lowNoteID = value; if(lowNoteID > highNoteID) { highNoteID = lowNoteID; } CreatePianoKeys(); InitializePianoKeys(); } } public int HighNoteID { get { return highNoteID; } set { if(value < 0 || value > ShortMessage.DataMaxValue) { throw new ArgumentOutOfRangeException("HighNoteID", value, "High note ID out of range."); } if(value == highNoteID) { return; } highNoteID = value; if(highNoteID < lowNoteID) { lowNoteID = highNoteID; } CreatePianoKeys(); InitializePianoKeys(); } } public Color NoteOnColor { get { return noteOnColor; } set { noteOnColor = value; foreach(PianoKey key in keys) { key.NoteOnColor = noteOnColor; } } } } }