diff --git a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml
index 1883b8ed..4e9d4233 100644
--- a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml
+++ b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml
@@ -17,6 +17,7 @@
+
@@ -29,6 +30,7 @@
+
@@ -41,6 +43,7 @@
+
@@ -53,11 +56,24 @@
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.Designer.cs b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.Designer.cs
new file mode 100644
index 00000000..04b59695
--- /dev/null
+++ b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.Designer.cs
@@ -0,0 +1,71 @@
+//
+// PianoRollEditor.Designer.cs
+//
+// Author:
+// Mike Becker
+//
+// Copyright (c) 2019 Mike Becker
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+
+using MBS.Framework.UserInterface;
+using MBS.Framework.UserInterface.Controls;
+using MBS.Framework.UserInterface.Layouts;
+
+using UniversalEditor.UserInterface;
+
+using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized;
+
+using UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Controls;
+using UniversalEditor.ObjectModels.Markup;
+
+namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll
+{
+ partial class PianoRollEditor : Editor
+ {
+ public PianoRollEditor()
+ {
+ InitializeComponent();
+ }
+
+ private static EditorReference _er = null;
+ public override EditorReference MakeReference()
+ {
+ if (_er == null)
+ {
+ _er = base.MakeReference();
+ _er.SupportedObjectModels.Add(typeof(SynthesizedAudioObjectModel));
+ }
+ return _er;
+ }
+
+ private PianoRollView PianoRoll = null;
+
+ protected override void OnObjectModelChanged(EventArgs e)
+ {
+ base.OnObjectModelChanged(e);
+
+
+ }
+
+ private void InitializeComponent()
+ {
+ this.Layout = new BoxLayout(Orientation.Vertical);
+
+ PianoRoll = new PianoRollView();
+ this.Controls.Add(PianoRoll, new BoxLayout.Constraints(true, true));
+ }
+ }
+}
diff --git a/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.cs b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.cs
new file mode 100644
index 00000000..92566348
--- /dev/null
+++ b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.cs
@@ -0,0 +1,57 @@
+//
+// PianoRollEditor.cs
+//
+// Author:
+// Mike Becker
+//
+// Copyright (c) 2019 Mike Becker
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using UniversalEditor.UserInterface;
+
+namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll
+{
+ public partial class PianoRollEditor
+ {
+ public override void UpdateSelections()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override EditorSelection CreateSelectionInternal(object content)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void OnToolboxItemSelected(ToolboxItemEventArgs e)
+ {
+ base.OnToolboxItemSelected(e);
+
+ switch (e.Item.Name)
+ {
+ case "ToolboxItem_Select":
+ {
+ this.PianoRoll.SelectionMode = PianoRollSelectionMode.Select;
+ break;
+ }
+ case "ToolboxItem_Insert":
+ {
+ this.PianoRoll.SelectionMode = PianoRollSelectionMode.Insert;
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollSelectionMode.cs b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollSelectionMode.cs
new file mode 100644
index 00000000..5dfb287e
--- /dev/null
+++ b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollSelectionMode.cs
@@ -0,0 +1,30 @@
+//
+// PianoRollSelectionMode.cs
+//
+// Author:
+// Mike Becker
+//
+// Copyright (c) 2019 Mike Becker
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll
+{
+ public enum PianoRollSelectionMode
+ {
+ Select,
+ Insert,
+ Delete
+ }
+}
diff --git a/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/Views/PianoRollView.cs b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/Views/PianoRollView.cs
new file mode 100644
index 00000000..15b6aeb4
--- /dev/null
+++ b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/Views/PianoRollView.cs
@@ -0,0 +1,602 @@
+//
+// PianoRollControl.cs
+//
+// Author:
+// Mike Becker
+//
+// Copyright (c) 2019 Mike Becker
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using System.Collections.Generic;
+using MBS.Framework.Drawing;
+using MBS.Framework.UserInterface;
+using MBS.Framework.UserInterface.Drawing;
+using MBS.Framework.UserInterface.Input.Keyboard;
+using MBS.Framework.UserInterface.Input.Mouse;
+using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized;
+
+namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Controls
+{
+ public class PianoRollView : /*View*/ CustomControl
+ {
+ private Menu contextMenu = null;
+ public PianoRollView()
+ {
+ this.contextMenu = new Menu();
+ this.contextMenu.Items.AddRange(new MenuItem[]
+ {
+ new CommandMenuItem("_Arrow", null, ContextMenuArrow_Click),
+ new CommandMenuItem("Dra_w", null, ContextMenuPencil_Click),
+ new CommandMenuItem("_Erase", null, ContextMenuErase_Click),
+ new SeparatorMenuItem(),
+ new CommandMenuItem("_Note Fixed Length", new MenuItem[] // disables drawing note length, forcing to specified length
+ {
+ new CommandMenuItem("ContextMenu_NoteFixedLength_1", "1/1 [1920]", null, ContextMenu_NoteFixedLength_Click),
+ new CommandMenuItem("ContextMenu_NoteFixedLength_2", "1/2 [960]", null, ContextMenu_NoteFixedLength_Click),
+ new CommandMenuItem("ContextMenu_NoteFixedLength_4", "1/4 [480]", null, ContextMenu_NoteFixedLength_Click),
+ new CommandMenuItem("ContextMenu_NoteFixedLength_8", "1/8 [240]", null, ContextMenu_NoteFixedLength_Click),
+ new CommandMenuItem("ContextMenu_NoteFixedLength_16", "1/16 [120]", null, ContextMenu_NoteFixedLength_Click),
+ new CommandMenuItem("ContextMenu_NoteFixedLength_32", "1/32 [60]", null, ContextMenu_NoteFixedLength_Click),
+ new CommandMenuItem("ContextMenu_NoteFixedLength_64", "1/64 [30]", null, ContextMenu_NoteFixedLength_Click),
+ new CommandMenuItem("ContextMenu_NoteFixedLength_Off", "Off", null, ContextMenu_NoteFixedLength_Click),
+ new SeparatorMenuItem(),
+ new CommandMenuItem("Triplet"),
+ new CommandMenuItem("Dot")
+ }),
+ new CommandMenuItem("_Quantize", new MenuItem[] // controls the quantize of position when drawing a new note
+ {
+ new CommandMenuItem("1/4"),
+ new CommandMenuItem("1/8"),
+ new CommandMenuItem("1/16"),
+ new CommandMenuItem("1/32"), // default
+ new CommandMenuItem("1/64"),
+ new CommandMenuItem("Off"),
+ new SeparatorMenuItem(),
+ new CommandMenuItem("Triplet")
+ }),
+ new CommandMenuItem("_Length", new MenuItem[] // controls the quantize of length when drawing a new note
+ {
+ new CommandMenuItem("1/4"),
+ new CommandMenuItem("1/8"),
+ new CommandMenuItem("1/16"),
+ new CommandMenuItem("1/32"), // default
+ new CommandMenuItem("1/64"),
+ new CommandMenuItem("Off"),
+ new SeparatorMenuItem(),
+ new CommandMenuItem("Triplet")
+ }),
+ new CommandMenuItem("_Show Grid Lines", null, ContextMenuToggleGridLines_Click), // toggles grid lines for quantized positions
+ new SeparatorMenuItem(),
+ new CommandMenuItem("_Undo"), // should be CommandReferenceCommandItem for EditUndo application-global command
+ new CommandMenuItem("Cu_t"),
+ new CommandMenuItem("_Copy"),
+ new CommandMenuItem("_Paste"),
+ new CommandMenuItem("_Delete"),
+ new SeparatorMenuItem(),
+ new CommandMenuItem("Select _All"),
+ new CommandMenuItem("Select All _Events"),
+ new SeparatorMenuItem(),
+ new CommandMenuItem("Insert _Lyrics..."),
+ new SeparatorMenuItem(),
+ new CommandMenuItem("_Properties...") // displays Note Property dialog
+ });
+ this.ContextMenu = contextMenu;
+ }
+
+ ///
+ /// When greater than -1, specifies the note length at which all newly-created notes are set and prevents dragging to specify note length.
+ ///
+ /// The length of a newly-created note.
+ public int FixedNoteLength { get; set; } = -1;
+
+ private int _PositionQuantization = -1;
+ public int PositionQuantization
+ {
+ get { return _PositionQuantization; }
+ set
+ {
+ _PositionQuantization = value;
+ Refresh();
+ }
+ }
+
+ private void ContextMenu_NoteFixedLength_Click(object sender, EventArgs e)
+ {
+ MenuItem mi = (sender as MenuItem);
+ string sValue = mi.Name.Substring("ContextMenu_NoteFixedLength_".Length);
+ int iValue = -1;
+ if (sValue != "Off")
+ {
+ iValue = Int32.Parse(sValue);
+ }
+ }
+
+ protected override void OnBeforeContextMenu(EventArgs e)
+ {
+ base.OnBeforeContextMenu(e);
+
+ contextMenu.Items[10].Visible = (SelectedCommands.Count > 0);
+ contextMenu.Items[11].Visible = (SelectedCommands.Count > 0);
+ contextMenu.Items[12].Visible = (SelectedCommands.Count > 0);
+ contextMenu.Items[13].Visible = (SelectedCommands.Count > 0);
+ }
+
+ private void ContextMenuArrow_Click(object sender, EventArgs e)
+ {
+ SelectionMode = PianoRollSelectionMode.Select;
+ }
+ private void ContextMenuPencil_Click(object sender, EventArgs e)
+ {
+ SelectionMode = PianoRollSelectionMode.Insert;
+ }
+ private void ContextMenuErase_Click(object sender, EventArgs e)
+ {
+ SelectionMode = PianoRollSelectionMode.Delete;
+ }
+ private void ContextMenuToggleGridLines_Click(object sender, EventArgs e)
+ {
+ ShowGridLines = !ShowGridLines;
+ }
+
+ private bool _ShowGridLines = false;
+ public bool ShowGridLines { get { return _ShowGridLines; } set { _ShowGridLines = value; Refresh(); } }
+
+ private PianoRollSelectionMode _SelectionMode;
+ public PianoRollSelectionMode SelectionMode
+ {
+ get { return _SelectionMode; }
+ set
+ {
+ _SelectionMode = value;
+ switch (_SelectionMode)
+ {
+ case PianoRollSelectionMode.Insert:
+ {
+ Cursor = Cursors.Pencil;
+ break;
+ }
+ case PianoRollSelectionMode.Select:
+ {
+ Cursor = Cursors.Default;
+ break;
+ }
+ case PianoRollSelectionMode.Delete:
+ {
+ Cursor = Cursors.Eraser;
+ break;
+ }
+ }
+ }
+ }
+
+ private SynthesizedAudioCommand.SynthesizedAudioCommandCollection mvarCommands = new SynthesizedAudioCommand.SynthesizedAudioCommandCollection();
+ public SynthesizedAudioCommand.SynthesizedAudioCommandCollection Commands { get { return mvarCommands; } }
+
+ private SynthesizedAudioCommand.SynthesizedAudioCommandCollection mvarSelectedCommands = new SynthesizedAudioCommand.SynthesizedAudioCommandCollection();
+ public SynthesizedAudioCommand.SynthesizedAudioCommandCollection SelectedCommands { get { return mvarSelectedCommands; } }
+
+ public SynthesizedAudioCommand HitTest(Vector2D pt)
+ {
+ return HitTest((int)pt.X, (int)pt.Y);
+ }
+ public SynthesizedAudioCommand HitTest(int x, int y)
+ {
+ foreach (SynthesizedAudioCommand cmd in mvarCommands)
+ {
+ Rectangle rect = GetCommandRect(cmd);
+ if (rect.Contains(x, y)) return cmd;
+ }
+ return null;
+ }
+ public SynthesizedAudioCommand[] HitTest(Rectangle rect)
+ {
+ List list = new List();
+ foreach (SynthesizedAudioCommand cmd in mvarCommands)
+ {
+ Rectangle rect1 = GetCommandRect(cmd);
+ if (rect.Normalize().IntersectsWith(rect1)) list.Add(cmd);
+ }
+ return list.ToArray();
+ }
+
+ private SynthesizedAudioTrack _SelectedTrack = null;
+ public SynthesizedAudioTrack SelectedTrack
+ {
+ get { return _SelectedTrack; }
+ set { _SelectedTrack = value; Refresh(); }
+ }
+
+ public int GridSize { get; set; } = 4;
+ public Dimension2D NoteSize { get; set; } = new Dimension2D(12, 16);
+
+ private Pen pGrid = new Pen(Colors.LightGray);
+ private Pen pGridAlt = new Pen(Colors.Gray);
+
+ private Pen pSelectionBorder = new Pen(Colors.SteelBlue);
+ private SolidBrush bSelectionFill = new SolidBrush(Color.FromRGBADouble(Colors.SteelBlue.R, Colors.SteelBlue.G, Colors.SteelBlue.B, 0.5));
+
+ private Pen pSelectionBorderDrawing = new Pen(Colors.DarkGray);
+ private SolidBrush bSelectionFillDrawing = new SolidBrush(Color.FromRGBADouble(Colors.DarkGray.R, Colors.DarkGray.G, Colors.DarkGray.B, 0.5));
+
+ private bool mvarShowKeyboard = true;
+ public bool ShowKeyboard { get { return mvarShowKeyboard; } set { mvarShowKeyboard = value; Invalidate(); } }
+
+ private int mvarKeyboardWidth = 64;
+ public int KeyboardWidth { get { return mvarKeyboardWidth; } set { mvarKeyboardWidth = value; Invalidate(); } }
+
+ private int mvarQuarterNoteWidth = 48;
+ private int mvarNoteHeight = 14;
+
+ private double mvarZoomFactor = 1.0;
+ public double ZoomFactor { get { return mvarZoomFactor; } set { mvarZoomFactor = value; Invalidate(); } }
+
+ private Dimension2D mvarQuantizationSize = new Dimension2D(16, 16);
+ public Dimension2D QuantizationSize { get { return mvarQuantizationSize; } set { mvarQuantizationSize = value; Invalidate(); } }
+
+ private bool m_selecting = false;
+ private double cx = 0, cy = 0;
+ private double dx = 0, dy = 0;
+
+ private Vector2D drag_OriginalLocation = new Vector2D(0, 0);
+ private Vector2D drag_CurrentLocation = new Vector2D(0, 0);
+
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ base.OnMouseDown(e);
+
+ SynthesizedAudioCommand cmd = HitTest(e.Location);
+ if ((e.ModifierKeys == KeyboardModifierKey.None && e.Buttons == MouseButtons.Primary) || cmd == null)
+ {
+ mvarSelectedCommands.Clear();
+ }
+ if (cmd != null)
+ {
+ bool remove = false;
+ if (e.ModifierKeys == KeyboardModifierKey.Shift)
+ {
+ if (mvarSelectedCommands.Count > 0)
+ {
+ int startIndex = mvarCommands.IndexOf(mvarSelectedCommands[mvarSelectedCommands.Count - 1]);
+ int endIndex = mvarCommands.IndexOf(cmd);
+
+ mvarSelectedCommands.Clear();
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ mvarSelectedCommands.Add(mvarCommands[i]);
+ }
+ }
+ }
+ else if (e.ModifierKeys == KeyboardModifierKey.Control)
+ {
+ remove = mvarSelectedCommands.Contains(cmd);
+ }
+ if (remove)
+ {
+ mvarSelectedCommands.Remove(cmd);
+ }
+ else
+ {
+ mvarSelectedCommands.Add(cmd);
+ }
+ }
+
+ if (e.Buttons == MouseButtons.Primary)
+ {
+ if (mvarShowKeyboard && e.X < mvarKeyboardWidth)
+ {
+ return;
+ }
+
+ cx = e.X;
+ cy = e.Y;
+ dx = e.X;
+ dy = e.Y;
+ m_selecting = true;
+
+ drag_OriginalLocation = Quantize(new Vector2D(cx, cy));
+ drag_CurrentLocation = drag_OriginalLocation;
+ }
+ Refresh();
+ }
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
+
+ SynthesizedAudioCommand cmd = HitTest(e.Location);
+ if (ShowKeyboard && e.X < KeyboardWidth || cmd != null)
+ {
+ Cursor = Cursors.Default;
+ }
+ else if (SelectionMode == PianoRollSelectionMode.Insert)
+ {
+ Cursor = Cursors.Pencil;
+ }
+
+ if (e.Buttons == MouseButtons.Primary)
+ {
+ dx = e.X;
+ dy = e.Y;
+
+ if (ShowKeyboard && dx < KeyboardWidth)
+ dx = KeyboardWidth;
+
+ if (SelectionMode == PianoRollSelectionMode.Select || SelectionMode == PianoRollSelectionMode.Insert)
+ {
+ drag_CurrentLocation = Quantize(new Vector2D(dx, drag_OriginalLocation.Y));
+
+ if (SelectionMode == PianoRollSelectionMode.Select)
+ {
+ Rectangle rectSelection = new Rectangle(cx, cy, dx - cx, dy - cy);
+ SynthesizedAudioCommand[] cmds = HitTest(rectSelection);
+ mvarSelectedCommands.Clear();
+ foreach (SynthesizedAudioCommand cmd1 in cmds)
+ {
+ mvarSelectedCommands.Add(cmd1);
+ }
+ }
+ Refresh();
+ }
+ }
+ }
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ base.OnMouseUp(e);
+
+ if (e.Buttons == MouseButtons.Primary)
+ {
+ if (SelectionMode == PianoRollSelectionMode.Insert)
+ {
+ if (drag_CurrentLocation.X - drag_OriginalLocation.X > 0)
+ {
+ SynthesizedAudioCommandNote note = new SynthesizedAudioCommandNote();
+
+ note.Position = (int)drag_OriginalLocation.X;
+ note.Length = (drag_CurrentLocation.X - drag_OriginalLocation.X);
+ note.Frequency = ValueToFrequency((int)drag_OriginalLocation.Y);
+ mvarCommands.Add(note);
+
+ mvarSelectedCommands.Clear();
+ mvarSelectedCommands.Add(note);
+ }
+ }
+ m_selecting = false;
+ Refresh();
+ }
+ }
+
+ protected override void OnMouseDoubleClick(MouseEventArgs e)
+ {
+ base.OnMouseDoubleClick(e);
+ if (e.Buttons == MouseButtons.Primary)
+ {
+ SynthesizedAudioCommand cmd = HitTest(e.Location);
+ if (cmd is SynthesizedAudioCommandNote)
+ {
+ SynthesizedAudioCommandNote note = (cmd as SynthesizedAudioCommandNote);
+ Rectangle rect = GetNoteRect(note);
+
+ /*
+ txtLyric.Location = rect.Location;
+ txtLyric.Size = rect.Size;
+ txtLyric.Text = note.Lyric;
+
+ txtLyric.Enabled = true;
+ txtLyric.Visible = true;
+
+ txtLyric.Focus();
+ */
+ }
+ }
+ }
+
+ int[] noteValuesBlackKeys = new int[] { 1, 3, 5, 8, 10 };
+
+ private Vector2D Quantize(Vector2D pt)
+ {
+ if (mvarShowKeyboard)
+ pt.X -= mvarKeyboardWidth;
+
+ int qX = (int)((double)(pt.X / mvarQuarterNoteWidth));
+ int qY = (int)((double)((pt.Y / mvarNoteHeight)));
+ return new Vector2D(qX, qY);
+ }
+ private Vector2D Unquantize(Vector2D pt)
+ {
+ int uqX = (int)(pt.X * mvarQuarterNoteWidth);
+ int uqY = (int)(pt.Y * mvarNoteHeight);
+ return new Vector2D(uqX, uqY);
+ }
+
+ private Rectangle GetKeyboardRect()
+ {
+ Rectangle keyboardRect = new Rectangle(0, 0, Size.Width, Size.Height); // ClientRectangle
+ keyboardRect.Width = mvarKeyboardWidth;
+ return keyboardRect;
+ }
+ private Rectangle GetGridRect()
+ {
+ Rectangle gridRect = new Rectangle(0, 0, Size.Width, Size.Height); // ClientRectangle;
+ if (mvarShowKeyboard)
+ {
+ Rectangle keyboardRect = GetKeyboardRect();
+ gridRect.X += keyboardRect.X + keyboardRect.Width;
+ }
+ return gridRect;
+ }
+ private Rectangle GetCommandRect(SynthesizedAudioCommand cmd)
+ {
+ if (cmd is SynthesizedAudioCommandNote)
+ {
+ return GetNoteRect(cmd as SynthesizedAudioCommandNote);
+ }
+ return Rectangle.Empty;
+ }
+ private Rectangle GetNoteRect(SynthesizedAudioCommandNote note)
+ {
+ Rectangle gridRect = GetGridRect();
+ Vector2D pt1 = Unquantize(new Vector2D((int)(note.Position), FrequencyToValue(note.Frequency)));
+ Vector2D pt2 = Unquantize(new Vector2D((int)note.Length, FrequencyToValue(note.Frequency)));
+ return new Rectangle(gridRect.X + pt1.X + 1, gridRect.Y + pt1.Y + 1, pt2.X - 1, mvarNoteHeight - 1);
+ }
+
+ private double ValueToFrequency(int value)
+ {
+ return value;
+ }
+ private int FrequencyToValue(double frequency)
+ {
+ return (int)frequency;
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ base.OnPaint(e);
+ int gridWidth = (int)(mvarQuarterNoteWidth * mvarZoomFactor);
+ int gridHeight = (int)(mvarNoteHeight * mvarZoomFactor);
+
+ Rectangle gridRect = GetGridRect();
+ if (mvarShowKeyboard)
+ {
+ DrawKeyboard(e.Graphics);
+ }
+
+ Pen gridPen = new Pen(Colors.LightGray);
+
+ for (int i = 0; i < Size.Height; i += gridHeight)
+ {
+ int noteValue = (int)(((double)i / gridHeight) % 12) + 1;
+ if (Array.IndexOf(noteValuesBlackKeys, noteValue) != -1)
+ {
+ e.Graphics.FillRectangle(new SolidBrush(Color.FromRGBAByte(0xDD, 0xDD, 0xDD)), new Rectangle(gridRect.X, gridRect.Y + i, gridRect.Width, gridHeight));
+ }
+
+ gridPen.Color = Colors.LightGray;
+ e.Graphics.DrawLine(gridPen, gridRect.X, gridRect.Y + i, gridRect.Right, gridRect.Y + i);
+
+ string[] noteNames = new string[]
+ {
+ "A#", "A", "G#", "G", "F#", "F", "E", "D#", "D", "C#", "C", "B"
+ };
+ if ((noteValue - 1) >= 0 && (noteValue - 1) < noteNames.Length)
+ {
+ string noteName = noteNames[noteValue - 1];
+ if (noteName.EndsWith("#")) continue;
+ if (!noteName.Equals("C")) continue;
+
+ e.Graphics.DrawText(noteName, Font, new Vector2D(mvarKeyboardWidth - 24, i + 1), new SolidBrush(noteName.Equals("C") ? Colors.DarkGray : Colors.LightGray));
+ }
+
+ e.Graphics.DrawText(noteValue.ToString(), Font, new Vector2D(0, i), new SolidBrush(Colors.Red));
+ }
+ for (int i = 0; i < Size.Width; i += (ShowGridLines ? (gridWidth / 2) : gridWidth))
+ {
+ if ((i % (mvarQuarterNoteWidth * 4)) == 0)
+ {
+ gridPen.Color = Colors.DarkGray;
+ }
+ else
+ {
+ gridPen.Color = Colors.LightGray;
+ }
+ e.Graphics.DrawLine(gridPen, gridRect.X + i, gridRect.Y, gridRect.X + i, gridRect.Bottom);
+ }
+
+ foreach (SynthesizedAudioCommand cmd in mvarCommands)
+ {
+ if (cmd is SynthesizedAudioCommandNote)
+ {
+ SynthesizedAudioCommandNote note = (cmd as SynthesizedAudioCommandNote);
+
+ Rectangle rect = GetNoteRect(note);
+
+ if (mvarSelectedCommands.Contains(cmd))
+ {
+ e.Graphics.FillRectangle(new SolidBrush(Color.FromRGBAByte(0xCC, 0xCC, 0xFF)), rect);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(new SolidBrush(Color.FromRGBAByte(0xCC, 0xFF, 0xCC)), rect);
+ }
+ e.Graphics.DrawRectangle(new Pen(Colors.DarkGray), rect);
+
+ Rectangle textRect = new Rectangle(rect.X + 2, rect.Y + 1, rect.Width - 4, rect.Height - 2);
+ e.Graphics.DrawText(note.Lyric, Font, textRect, new SolidBrush(Colors.Black));
+ }
+ }
+
+ if (m_selecting)
+ {
+ if (SelectionMode == PianoRollSelectionMode.Select)
+ {
+ e.Graphics.DrawRectangle(pSelectionBorder, new Rectangle(cx, cy, dx - cx, dy - cy));
+ e.Graphics.FillRectangle(bSelectionFill, new Rectangle(cx, cy, dx - cx, dy - cy));
+ }
+ else if (SelectionMode == PianoRollSelectionMode.Insert)
+ {
+ Rectangle dragrect = new Rectangle(drag_OriginalLocation.X * mvarQuarterNoteWidth, drag_OriginalLocation.Y * mvarNoteHeight, (drag_CurrentLocation.X * mvarQuarterNoteWidth) - (drag_OriginalLocation.X * mvarQuarterNoteWidth), mvarNoteHeight);
+ if (mvarShowKeyboard)
+ {
+ dragrect.X += mvarKeyboardWidth;
+ }
+
+ e.Graphics.DrawRectangle(pSelectionBorderDrawing, dragrect);
+ e.Graphics.FillRectangle(bSelectionFillDrawing, dragrect);
+ }
+ }
+
+ /*
+ // draw grids
+ for (int i = 0; i < Size.Width; i += (int) NoteSize.Width)
+ {
+ if (!ShowGridLines && (i % (int)(8 * NoteSize.Width) != 0))
+ continue;
+ e.Graphics.DrawLine(((i % (int)(8 * NoteSize.Width)) == 0) ? pGridAlt : pGrid, i, 0, i, Size.Height);
+ }
+ for (int i = 0; i < Size.Height; i += (int)NoteSize.Height)
+ {
+ e.Graphics.DrawLine(pGrid, 0, i, Size.Width, i);
+ }
+ */
+ }
+
+ private void DrawKeyboard(Graphics g)
+ {
+ int gridWidth = (int)(mvarQuarterNoteWidth * mvarZoomFactor);
+ int gridHeight = (int)(mvarNoteHeight * mvarZoomFactor);
+
+ Rectangle keyboardRect = GetKeyboardRect();
+ for (int i = 0; i < keyboardRect.Height; i += gridHeight)
+ {
+ int noteValue = (int)(((double)i / gridHeight) % 12) + 1;
+ g.DrawLine(new Pen(Colors.LightGray), keyboardRect.X, i, keyboardRect.Right, i);
+
+ if (Array.IndexOf(noteValuesBlackKeys, noteValue) != -1)
+ {
+ // has a black key
+ g.FillRectangle(new SolidBrush(Colors.DarkGray), keyboardRect.X, i, (int)((double)keyboardRect.Width / 2), gridHeight);
+ }
+ }
+ }
+
+ private int QuantizeHeight(int cy)
+ {
+ return (int)((double)cy / mvarNoteHeight);
+ }
+ private int QuantizeWidth(int cy)
+ {
+ return (int)((double)cy / mvarQuarterNoteWidth);
+ }
+ }
+}
diff --git a/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj
index deff192c..f366ea2a 100644
--- a/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj
+++ b/CSharp/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj
@@ -34,6 +34,10 @@
+
+
+
+
@@ -42,6 +46,10 @@
+
+
+
+