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 @@ + + + +