diff --git a/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml b/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml index 4b0f0e0b..21a22fa0 100644 --- a/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml +++ b/Content/UniversalEditor.Content.PlatformIndependent/Editors/Multimedia/Audio/Synthesized/PianoRoll/MenuBar.uexml @@ -129,24 +129,26 @@ - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + + - + @@ -215,9 +217,9 @@ - - + + diff --git a/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/Vocaloid.uexml b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VPR.uexml similarity index 61% rename from Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/Vocaloid.uexml rename to Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VPR.uexml index fb009001..e68626f7 100644 --- a/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/Vocaloid.uexml +++ b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VPR.uexml @@ -3,24 +3,29 @@ - + - *.vsq + *.vpr + + + PK + + - + - + - *.vsqx + sequence.json @@ -28,7 +33,7 @@ - + diff --git a/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VSQ.uexml b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VSQ.uexml new file mode 100644 index 00000000..8f8ae081 --- /dev/null +++ b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VSQ.uexml @@ -0,0 +1,20 @@ + + + + + + + + *.vsq + + + + + + + + + + + + \ No newline at end of file diff --git a/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VSQX.uexml b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VSQX.uexml new file mode 100644 index 00000000..8aceac08 --- /dev/null +++ b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/AudioWorkstation/Extensions/Vocaloid/Associations/SynthesizedAudio/VSQX.uexml @@ -0,0 +1,20 @@ + + + + + + + + *.vsqx + + + + + + + + + + + + \ No newline at end of file diff --git a/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj b/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj index 8515efcd..fa9e9775 100644 --- a/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj +++ b/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj @@ -728,8 +728,10 @@ - + + + diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.Designer.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.Designer.cs index ad1a8cf0..eb95c936 100644 --- a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.Designer.cs +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.Designer.cs @@ -26,7 +26,7 @@ using UniversalEditor.UserInterface; using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized; -using UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Controls; +using UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Views; namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll { diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.cs index ab7d3687..edafb586 100644 --- a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.cs +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/PianoRollEditor.cs @@ -32,7 +32,16 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll { public override void UpdateSelections() { - throw new NotImplementedException(); + Selections.Clear(); + if (PianoRoll.SelectedCommands.Count > 0) + { + PianoRollEditorSelection sel = new PianoRollEditorSelection(this, PianoRoll); + for (int i = 0; i < PianoRoll.SelectedCommands.Count; i++) + { + sel.Commands.Add(PianoRoll.SelectedCommands[i]); + } + Selections.Add(sel); + } } protected override EditorSelection CreateSelectionInternal(object content) @@ -40,6 +49,11 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll throw new NotImplementedException(); } + protected override void OnDocumentEdited(EventArgs e) + { + base.OnDocumentEdited(e); + PianoRoll.Refresh(); + } protected override void OnObjectModelChanged(EventArgs e) { base.OnObjectModelChanged(e); diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/QuantizationMode.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/QuantizationMode.cs new file mode 100644 index 00000000..889442cc --- /dev/null +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/QuantizationMode.cs @@ -0,0 +1,29 @@ +// +// QuantizationMode.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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 QuantizationMode + { + Length, + Position + } +} diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/Views/PianoRollView.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/Views/PianoRollView.cs index 627f2a98..972fee0c 100644 --- a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/Views/PianoRollView.cs +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRoll/Views/PianoRollView.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; +using System.Linq; using MBS.Framework.Drawing; using MBS.Framework.UserInterface; @@ -34,7 +35,7 @@ using UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Dialogs; using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized; using UniversalEditor.UserInterface; -namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Controls +namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Views { /// /// Provides a UWT-based for manipulating s in a piano roll style. @@ -60,6 +61,23 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Draw", ContextMenuPencil_Click); Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Erase", ContextMenuErase_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Quantize_4", ContextMenu_Quantize_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Quantize_8", ContextMenu_Quantize_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Quantize_16", ContextMenu_Quantize_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Quantize_32", ContextMenu_Quantize_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Quantize_64", ContextMenu_Quantize_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Quantize_Off", ContextMenu_Quantize_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_Quantize_Triplet", ContextMenu_Quantize_Click); + + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_4", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_8", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_16", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_32", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_64", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_Off", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_Default", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteLength_Triplet", ContextMenu_NoteLength_Click); + Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteFixedLength_1", ContextMenu_NoteFixedLength_Click); Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteFixedLength_2", ContextMenu_NoteFixedLength_Click); Application.AttachCommandEventHandler("PianoRollEditor_ContextMenu_NoteFixedLength_4", ContextMenu_NoteFixedLength_Click); @@ -152,17 +170,6 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control /// 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) { Command cmd = (sender as Command); @@ -190,6 +197,65 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control } } } + private void ContextMenu_Quantize_Click(object sender, EventArgs e) + { + Command cmd = (sender as Command); + string sValue = cmd.ID.Substring("PianoRollEditor_ContextMenu_Quantize_".Length); + int iValue = 4; //-1; + + switch (sValue) + { + case "Off": + { + break; + } + case "Triplet": + { + break; + } + case "Dot": + { + break; + } + default: + { + iValue = Int32.Parse(sValue); + break; + } + } + + PositionQuantization = (int)((double)1920 / iValue); + } + private void ContextMenu_NoteLength_Click(object sender, EventArgs e) + { + Command cmd = (sender as Command); + string sValue = cmd.ID.Substring("PianoRollEditor_ContextMenu_NoteLength_".Length); + int iValue = 4; //-1; + + switch (sValue) + { + case "Off": + { + break; + } + case "Triplet": + { + break; + } + case "Default": + { + iValue = -1; + break; + } + default: + { + iValue = Int32.Parse(sValue); + break; + } + } + + LengthQuantization = (int)((double)1920 / iValue); + } // TODO: make this more user-friendly private static Dictionary Scales = new Dictionary(); @@ -317,7 +383,6 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control 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); @@ -327,20 +392,54 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control private SolidBrush bSelectionFillDrawing = new SolidBrush(Color.FromRGBADouble(Colors.DarkGray.R, Colors.DarkGray.G, Colors.DarkGray.B, 0.5)); private bool mvarShowKeyboard = true; + /// + /// Gets or sets a value indicating whether the virtual keyboard should be shown on this . + /// + /// true if the virtual keyboard should be shown; otherwise, false. 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 = 22; + private int _LengthQuantization = -1; + /// + /// Gets or sets the quantization for note length (i.e., the width of a quarter note on the grid). All quantization operations are relative to this value. + /// + /// The quantization for note length (i.e., the width of a quarter note on the grid). + public int LengthQuantization { get { return _LengthQuantization; } set { _LengthQuantization = value; Invalidate(); } } + /// + /// Gets or sets the quantization for note positioning. + /// + /// The quantization for note positioning. + public int PositionQuantization { get; set; } = 60; + + private int GetLengthQuantization() + { + if (LengthQuantization == -1) + return PositionQuantization; + return LengthQuantization; + } + + private int _GridWidth = 120; + public int GridWidth { get { return _GridWidth; }set { _GridWidth = value; Invalidate(); } } + + private int _NoteHeight = 22; + /// + /// Gets or sets the height of a note on the grid. + /// + /// The height of a note on the grid. + public int NoteHeight { get { return _NoteHeight; } set { _NoteHeight = value; Invalidate(); } } + + private bool _HighlightOverlappingNotes = true; + /// + /// Gets or sets a value indicating whether overlapping notes on this grid are highlighted. + /// + /// true if overlapping notes should be highlighted; otherwise, false. + public bool HighlightOverlappingNotes { get { return _HighlightOverlappingNotes; } set { _HighlightOverlappingNotes = value; Invalidate(); } } 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; @@ -356,6 +455,7 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); + Focus(); if (SelectedTrack == null) return; @@ -411,10 +511,10 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control dy = e.Y; m_selecting = true; - drag_OriginalLocation = Quantize(new Vector2D(cx, cy)); + drag_OriginalLocation = Quantize(new Vector2D(cx, cy), QuantizationMode.Position); if (cmd is SynthesizedAudioCommandNote) { - note_OriginalLocation = Quantize(GetNoteRect(cmd as SynthesizedAudioCommandNote).Location); + note_OriginalLocation = Quantize(GetNoteRect(cmd as SynthesizedAudioCommandNote).Location, QuantizationMode.Position); } drag_CurrentLocation = drag_OriginalLocation; } @@ -447,7 +547,7 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control if (draggingCommand == null && (SelectionMode == PianoRollSelectionMode.Select || SelectionMode == PianoRollSelectionMode.Insert)) { - drag_CurrentLocation = Quantize(new Vector2D(dx, drag_OriginalLocation.Y)); + drag_CurrentLocation = Quantize(new Vector2D(dx, drag_OriginalLocation.Y), QuantizationMode.Length); if (SelectionMode == PianoRollSelectionMode.Select) { Rectangle rectSelection = new Rectangle(cx, cy, dx - cx, dy - cy); @@ -462,7 +562,7 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control } else { - drag_CurrentLocation = Quantize(new Vector2D(dx, dy)); + drag_CurrentLocation = Quantize(new Vector2D(dx, dy), QuantizationMode.Length); } Refresh(); } @@ -497,11 +597,21 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control double y = drag_CurrentLocation.Y - drag_OriginalLocation.Y; Rectangle origNoteRect = GetNoteRect(note); - Vector2D v = Quantize(origNoteRect.Location); + Vector2D v = Quantize(origNoteRect.Location, QuantizationMode.Position); v.X += x; v.Y += y; - ApplyNoteRect(ref note, v); + if ((e.ModifierKeys & KeyboardModifierKey.Control) == KeyboardModifierKey.Control) + { + // copy the note to the current location + SynthesizedAudioCommandNote note2 = note.Clone() as SynthesizedAudioCommandNote; + ApplyNoteRect(ref note2, v); + SelectedTrack.Commands.Add(note2); + } + else + { + ApplyNoteRect(ref note, v); + } } } @@ -526,8 +636,8 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control SynthesizedAudioCommandNote note = new SynthesizedAudioCommandNote(); - note.Position = (int)drag_OriginalLocation.X; - note.Length = (drag_CurrentLocation.X - drag_OriginalLocation.X); + note.Position = LocationToNotePosition((int)drag_OriginalLocation.X, QuantizationMode.Position) * 4; + note.Length = LocationToNotePosition((int)(drag_CurrentLocation.X - drag_OriginalLocation.X), QuantizationMode.Length) * 4; note.Frequency = ValueToFrequency((int)drag_OriginalLocation.Y); Editor.BeginEdit(); @@ -547,9 +657,18 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control } } + private int LocationToNotePosition(int x, QuantizationMode mode) + { + return (int)Unquantize(new MBS.Framework.Drawing.Vector2D(x, 0), mode).X; + } + private int NotePositionToLocation(double x) + { + return (int)(x / 4); + } + private void ApplyNoteRect(ref SynthesizedAudioCommandNote note, Vector2D quantized) { - note.Position = (int)quantized.X; + note.Position = LocationToNotePosition((int)quantized.X, QuantizationMode.Position) * 4; note.Frequency = ValueToFrequency((int)quantized.Y); } @@ -625,19 +744,31 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control "A#", "A", "G#", "G", "F#", "F", "E", "D#", "D", "C#", "C", "B" }; - private Vector2D Quantize(Vector2D pt) + private Vector2D Quantize(Vector2D pt, QuantizationMode mode) { if (mvarShowKeyboard) pt.X -= mvarKeyboardWidth; - int qX = (int)((double)(pt.X / mvarQuarterNoteWidth)); - int qY = (int)((double)((pt.Y / mvarNoteHeight))); + int quant; + if (mode == QuantizationMode.Length) + quant = GetLengthQuantization(); + else + quant = PositionQuantization; + + int qX = (int)((double)(pt.X / (quant * ZoomFactor))); + int qY = (int)((double)((pt.Y / NoteHeight))); return new Vector2D(qX, qY); } - private Vector2D Unquantize(Vector2D pt) + private Vector2D Unquantize(Vector2D pt, QuantizationMode mode) { - int uqX = (int)(pt.X * mvarQuarterNoteWidth); - int uqY = (int)(pt.Y * mvarNoteHeight); + int quant; + if (mode == QuantizationMode.Length) + quant = GetLengthQuantization(); + else + quant = PositionQuantization; + + int uqX = (int)(pt.X * (quant * ZoomFactor)); + int uqY = (int)(pt.Y * NoteHeight); return new Vector2D(uqX, uqY); } @@ -668,26 +799,46 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control 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); + + int x = (int)NotePositionToLocation(note.Position); + int y = FrequencyToValue(note.Frequency); + int width = (int)NotePositionToLocation((int)note.Length); + int height = NoteHeight; + + return new Rectangle(gridRect.X + x + 1, gridRect.Y + y + 1, width - 1, height - 1); } private double ValueToFrequency(int value) { - return value; + return (81 - value); } private int FrequencyToValue(double frequency) { - return (int)frequency; + return (int)((81 - frequency) * NoteHeight); + } + + private bool _scrollBoundsChanged = true; + private void RecalcScrollBounds() + { + for (int i = 0; i < SelectedTrack.Commands.Count; i++) + { + + } + } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); - int gridWidth = (int)(mvarQuarterNoteWidth * mvarZoomFactor); - int gridHeight = (int)(mvarNoteHeight * mvarZoomFactor); + int gridWidth = (int)(GridWidth * mvarZoomFactor); + int gridHeight = (int)(NoteHeight * mvarZoomFactor); + + if (_scrollBoundsChanged) + { + RecalcScrollBounds(); + _scrollBoundsChanged = false; + } Rectangle gridRect = GetGridRect(); if (mvarShowKeyboard) @@ -738,7 +889,7 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control } for (int i = 0; i < Size.Width; i += (ShowGridLines ? (gridWidth / 2) : gridWidth)) { - if ((i % (mvarQuarterNoteWidth * 4)) == 0) + if ((i % GetLengthQuantization()) == 0) { gridPen.Color = Colors.Gray.Alpha(0.50); } @@ -758,24 +909,10 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control SynthesizedAudioCommandNote note = (cmd as SynthesizedAudioCommandNote); Rectangle rect = GetNoteRect(note); - - if (mvarSelectedCommands.Contains(cmd)) - { - e.Graphics.FillRectangle(new SolidBrush(SystemColors.HighlightBackground), rect); - } - else - { - e.Graphics.FillRectangle(new SolidBrush(SystemColors.WindowBackground), rect); - e.Graphics.FillRectangle(new SolidBrush(Colors.Green.Alpha(0.3)), rect); - } - e.Graphics.DrawRectangle(new Pen(Colors.Green.Alpha(0.5)), rect); - - Rectangle textRect = new Rectangle(rect.X + 2, rect.Y + 10, rect.Width - 4, rect.Height - 2); - - if (!(txt.Visible && _EditingNote == cmd)) - { - e.Graphics.DrawText(String.Format("{0} [ {1} ]", note.Lyric, note.Phoneme), Font, textRect, new SolidBrush(mvarSelectedCommands.Contains(cmd) ? SystemColors.HighlightForeground : SystemColors.WindowForeground)); - } + bool overlaps = false; + if (HighlightOverlappingNotes) overlaps = NoteOverlaps(note); + if (!DrawNote(e.Graphics, rect, note.Lyric, note.Phoneme, mvarSelectedCommands.Contains(cmd), _EditingNote == note, overlaps)) + break; } } } @@ -786,7 +923,7 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control { Rectangle origNoteRect = GetNoteRect(draggingCommand as SynthesizedAudioCommandNote); Rectangle newNoteRect = GetNoteRect(draggingCommand as SynthesizedAudioCommandNote); - newNoteRect.Location = Unquantize(new Vector2D(note_OriginalLocation.X + (drag_CurrentLocation.X - drag_OriginalLocation.X), note_OriginalLocation.Y + (drag_CurrentLocation.Y - drag_OriginalLocation.Y))); + newNoteRect.Location = Unquantize(new Vector2D(note_OriginalLocation.X + (drag_CurrentLocation.X - drag_OriginalLocation.X), note_OriginalLocation.Y + (drag_CurrentLocation.Y - drag_OriginalLocation.Y)), QuantizationMode.Position); Vector2D diff = new Vector2D(newNoteRect.X - origNoteRect.X, newNoteRect.Y - origNoteRect.Y); foreach (SynthesizedAudioCommand cmd in mvarSelectedCommands) @@ -812,7 +949,7 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control } 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); + Rectangle dragrect = new Rectangle(drag_OriginalLocation.X * PositionQuantization, drag_OriginalLocation.Y * NoteHeight, (drag_CurrentLocation.X * GetLengthQuantization()) - (drag_OriginalLocation.X * GetLengthQuantization()), NoteHeight); if (mvarShowKeyboard) { dragrect.X += mvarKeyboardWidth; @@ -837,11 +974,58 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control } */ } + private bool NoteOverlaps(SynthesizedAudioCommandNote note) + { + Rectangle rect = GetNoteRect(note); + + for (int i = 0; i < SelectedTrack.Commands.Count; i++) + { + SynthesizedAudioCommandNote note2 = SelectedTrack.Commands[i] as SynthesizedAudioCommandNote; + if (note2 == null) continue; + if (note2 == note) continue; + + Rectangle rect2 = GetNoteRect(note2); + if ((rect.X >= rect2.X && rect.X <= rect2.Right) || (rect2.X >= rect.X & rect2.X <= rect.Right)) + return true; + } + return false; + } + + private bool DrawNote(Graphics graphics, Rectangle rect, string lyric, string phoneme, bool selected, bool editing, bool overlaps) + { + if (rect.X > Size.Width) + return false; // no need to continue since we've reached the end of the visible area + + Color noteColor = SystemColors.HighlightBackground.Darken(0.1); + if (overlaps) + { + noteColor = Colors.DarkGray; + } + + if (selected) + { + graphics.FillRectangle(new SolidBrush(SystemColors.HighlightBackground), rect); + } + else + { + graphics.FillRectangle(new SolidBrush(SystemColors.WindowBackground), rect); + graphics.FillRectangle(new SolidBrush(noteColor.Alpha(0.3)), rect); + } + graphics.DrawRectangle(new Pen(noteColor.Alpha(0.5)), rect); + + Rectangle textRect = new Rectangle(rect.X + 2, rect.Y + 10, rect.Width - 4, rect.Height - 2); + + if (!(txt.Visible && editing)) + { + graphics.DrawText(String.Format("{0} [ {1} ]", lyric, phoneme), Font, textRect, new SolidBrush(selected ? SystemColors.HighlightForeground : SystemColors.WindowForeground)); + } + return true; + } private void DrawKeyboard(Graphics g) { - int gridWidth = (int)(mvarQuarterNoteWidth * mvarZoomFactor); - int gridHeight = (int)(mvarNoteHeight * mvarZoomFactor); + int gridWidth = (int)(GetLengthQuantization() * mvarZoomFactor); + int gridHeight = (int)(NoteHeight * mvarZoomFactor); Rectangle keyboardRect = GetKeyboardRect(); for (int i = 0; i < keyboardRect.Height; i += gridHeight) @@ -864,11 +1048,11 @@ namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized.PianoRoll.Control private int QuantizeHeight(int cy) { - return (int)((double)cy / mvarNoteHeight); + return (int)((double)cy / NoteHeight); } private int QuantizeWidth(int cy) { - return (int)((double)cy / mvarQuarterNoteWidth); + return (int)((double)cy / GetLengthQuantization()); } } } diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRollEditorSelection.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRollEditorSelection.cs new file mode 100644 index 00000000..c9a68d1e --- /dev/null +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/Editors/Multimedia/Audio/Synthesized/PianoRollEditorSelection.cs @@ -0,0 +1,49 @@ +// +// PianoRollEditorSelection.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.Editors.Multimedia.Audio.Synthesized.PianoRoll; +using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized; +using UniversalEditor.UserInterface; + +namespace UniversalEditor.Editors.Multimedia.Audio.Synthesized +{ + public class PianoRollEditorSelection : EditorSelection + { + private PianoRoll.Views.PianoRollView _parent = null; + internal PianoRollEditorSelection(Editor editor, PianoRoll.Views.PianoRollView parent) : base(editor) + { + _parent = parent; + } + + public SynthesizedAudioCommand.SynthesizedAudioCommandCollection Commands { get; private set; } = new SynthesizedAudioCommand.SynthesizedAudioCommandCollection(); + public override object Content { get => Commands; set => Commands = (SynthesizedAudioCommand.SynthesizedAudioCommandCollection)value; } + + protected override void DeleteInternal() + { + _parent.Editor.BeginEdit(); + for (int i = 0; i < Commands.Count; i++) + { + _parent.SelectedTrack.Commands.Remove(Commands[i]); + } + _parent.Editor.EndEdit(); + } + } +} diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj index 190e57fb..b80eacac 100644 --- a/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface/UniversalEditor.Plugins.Multimedia.UserInterface.csproj @@ -48,6 +48,8 @@ + + diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Audio/Synthesized/UTAU/USTDataFormat.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Audio/Synthesized/UTAU/USTDataFormat.cs index e33fb047..2201fbab 100644 --- a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Audio/Synthesized/UTAU/USTDataFormat.cs +++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Audio/Synthesized/UTAU/USTDataFormat.cs @@ -72,27 +72,27 @@ namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.UTAU InitializePhonemeDictionary(); } + private static DataFormatReference _dfr = null; protected override DataFormatReference MakeReferenceInternal() { - DataFormatReference dfr = base.MakeReferenceInternal(); - dfr.Clear(); - - dfr.Capabilities.Add(typeof(PropertyListObjectModel), DataFormatCapabilities.Bootstrap); - dfr.Capabilities.Add(typeof(SynthesizedAudioObjectModel), DataFormatCapabilities.All); - return dfr; + if (_dfr == null) + { + _dfr = base.MakeReferenceInternal(); + _dfr.Capabilities.Add(typeof(SynthesizedAudioObjectModel), DataFormatCapabilities.All); + } + return _dfr; } protected override void BeforeLoadInternal(Stack objectModels) { base.BeforeLoadInternal(objectModels); - - // base.Accessor.DefaultEncoding = IO.Encoding.GetRuntimeEncoding(System.Text.Encoding.GetEncoding("shift-jis")); - throw new NotImplementedException(); + base.Accessor.DefaultEncoding = IO.Encoding.ShiftJIS; objectModels.Push(new PropertyListObjectModel()); } protected override void AfterLoadInternal(Stack objectModels) { base.AfterLoadInternal(objectModels); + PropertyListObjectModel plom = objectModels.Pop() as PropertyListObjectModel; SynthesizedAudioObjectModel sa = objectModels.Pop() as SynthesizedAudioObjectModel; @@ -105,6 +105,8 @@ namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.UTAU { SynthesizedAudioTrack track = new SynthesizedAudioTrack(); Group grp = plom.Items[groupStart] as Group; + + int position = 0; while (grp.Name != "#TRACKEND") { SynthesizedAudioCommandNote note = null; @@ -116,6 +118,7 @@ namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.UTAU { note = new SynthesizedAudioCommandNote(); } + note.Position = position; note.Length = int.Parse(grp.Items.OfType("Length").Value.ToString()); note.Lyric = grp.Items.OfType("Lyric").Value.ToString(); note.Phoneme = LyricToPhoneme(note.Lyric); @@ -126,25 +129,82 @@ namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.UTAU note.Intensity = grp.GetPropertyValue("Intensity"); note.Modulation = grp.GetPropertyValue("Moduration"); note.PBType = grp.GetPropertyValue("PBType"); - note.Pitches = grp.GetPropertyValue("Pitches").Split(new char[] { ',' }); - note.Envelope = grp.GetPropertyValue("Envelope").Split(new char[] { ',' }); - note.VBR = grp.GetPropertyValue("VBR").Split(new char[] { ',' }); + note.Pitches = grp.GetPropertyValue("Pitches", String.Empty).Split(new char[] { ',' }); + note.Envelope = grp.GetPropertyValue("Envelope", String.Empty).Split(new char[] { ',' }); + note.VBR = grp.GetPropertyValue("VBR", String.Empty).Split(new char[] { ',' }); track.Commands.Add(note); groupStart++; grp = plom.Items[groupStart] as Group; + + position += (int)note.Length; } sa.Tracks.Add(track); } } + protected override void BeforeSaveInternal(Stack objectModels) + { + base.BeforeSaveInternal(objectModels); + + SynthesizedAudioObjectModel sa = objectModels.Pop() as SynthesizedAudioObjectModel; + PropertyListObjectModel plom = new PropertyListObjectModel(); + + plom.Items.Add(new Group("#SETTING", new PropertyListItem[] + { + new Property("Tempo", sa.Tempo), + new Property("Tracks", sa.Tracks.Count.ToString()), + new Property("ProjectName", sa.Name), + new Property("VoiceDir", "%VOICE%"), + new Property("OutFile", System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(Accessor.GetFileName()), ".wav")), + new Property("CacheDir", ".cache"), + new Property("Tool1", "wavtool.exe"), + new Property("Tool2", "resampler.exe"), + new Property("Mode2", true), + new Property("Flags", "g8BRE10H10") + })); + + for (int i = 0; i < sa.Tracks.Count; i++) + { + for (int k = 0; k < sa.Tracks[i].Commands.Count; k++) + { + if (sa.Tracks[i].Commands[k] is SynthesizedAudioCommandNote) + { + SynthesizedAudioCommandNote note = sa.Tracks[i].Commands[k] as SynthesizedAudioCommandNote; + + Group gNote = new Group(String.Format("#{0}", i.ToString().PadLeft(4, '0')), new PropertyListItem[] + { + new Property("Length", note.Length), + new Property("Lyric", note.Lyric), + new Property("NoteNum", note.Frequency), + new Property("PreUtterance", note.PreUtterance), + new Property("VoiceOverlap", note.VoiceOverlap), + new Property("Intensity", note.Intensity), + new Property("Moduration", note.Modulation), + new Property("Envelope", String.Join(",", note.Envelope)) + }); + plom.Items.Add(gNote); + } + } + plom.Items.Add(new Group("#TRACKEND")); + } + + objectModels.Push(plom); + } + private string LyricToPhoneme(string lyric) { // Use PhonemeDictionary.xml lookup table to find the phoneme that corresponds to the // lyric - Phoneme p = mvarPhonemeDictionary.PhonemeLists[0].GetPhonemeFromMapping(lyric); - if (p == null) return null; - return p.Value; + if (mvarPhonemeDictionary.PhonemeLists.Count > 0) + { + for (int i = 0; i < mvarPhonemeDictionary.PhonemeLists.Count; i++) + { + Phoneme p = mvarPhonemeDictionary.PhonemeLists[i].GetPhonemeFromMapping(lyric); + if (p != null) return p.Value; + } + } + return null; } } } diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/ObjectModels/Multimedia/Audio/Synthesized/SynthesizedAudioCommandNote.cs b/Plugins/UniversalEditor.Plugins.Multimedia/ObjectModels/Multimedia/Audio/Synthesized/SynthesizedAudioCommandNote.cs index e4fc44b7..a9a7cf5a 100644 --- a/Plugins/UniversalEditor.Plugins.Multimedia/ObjectModels/Multimedia/Audio/Synthesized/SynthesizedAudioCommandNote.cs +++ b/Plugins/UniversalEditor.Plugins.Multimedia/ObjectModels/Multimedia/Audio/Synthesized/SynthesizedAudioCommandNote.cs @@ -66,6 +66,7 @@ namespace UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized PortamentoRising = this.PortamentoRising, PreUtterance = this.PreUtterance, Protected = this.Protected, + Position = this.Position, Frequency = this.Frequency, VBR = this.VBR.Clone() as double[], VoiceOverlap = this.VoiceOverlap diff --git a/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VPR/VPRProjectZIPDataFormat.cs b/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VPR/VPRProjectZIPDataFormat.cs new file mode 100644 index 00000000..f549b4eb --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VPR/VPRProjectZIPDataFormat.cs @@ -0,0 +1,96 @@ +// +// VPRProjectZIPDataFormat.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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.Collections.Generic; +using UniversalEditor.Accessors; +using UniversalEditor.DataFormats.FileSystem.ZIP; +using UniversalEditor.ObjectModels.FileSystem; +using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized; + +namespace UniversalEditor.Plugins.Vocaloid.DataFormats.Multimedia.Audio.Synthesized.Vocaloid.VPR +{ + public class VPRProjectZIPDataFormat : ZIPDataFormat + { + private static DataFormatReference _dfr = null; + protected override DataFormatReference MakeReferenceInternal() + { + if (_dfr == null) + { + _dfr = base.MakeReferenceInternal(); + _dfr.Capabilities.Add(typeof(SynthesizedAudioObjectModel), DataFormatCapabilities.All); + } + return _dfr; + } + + protected override void BeforeLoadInternal(Stack objectModels) + { + base.BeforeLoadInternal(objectModels); + objectModels.Push(new FileSystemObjectModel()); + } + protected override void AfterLoadInternal(Stack objectModels) + { + base.AfterLoadInternal(objectModels); + + FileSystemObjectModel fsom = (objectModels.Pop() as FileSystemObjectModel); + ObjectModel whatever = objectModels.Pop(); + + if (whatever is SynthesizedAudioObjectModel) + { + // have the VPRSequenceJSONDataFormat load the sequence.json into the synthesized audio object model + SynthesizedAudioObjectModel vsq = (whatever as SynthesizedAudioObjectModel); + VPRSequenceJSONDataFormat vpr = new VPRSequenceJSONDataFormat(); + + File sequence_json = fsom.FindFile("Project\\sequence.json"); + if (sequence_json == null) throw new InvalidDataFormatException("archive does not contain a file called 'Project\\sequence.json'"); + + MemoryAccessor ma = new MemoryAccessor(sequence_json.GetData()); + Document.Load(vsq, vpr, ma); + } + else + { + // huh? + } + } + protected override void BeforeSaveInternal(Stack objectModels) + { + base.BeforeSaveInternal(objectModels); + + ObjectModel whatever = objectModels.Pop(); + FileSystemObjectModel fsom = new FileSystemObjectModel(); + + if (whatever is SynthesizedAudioObjectModel) + { + // have the VPRSequenceJSONDataFormat save the sequence.json and just add it as the only file in the ZIP + SynthesizedAudioObjectModel vsq = (whatever as SynthesizedAudioObjectModel); + VPRSequenceJSONDataFormat vpr = new VPRSequenceJSONDataFormat(); + MemoryAccessor ma = new MemoryAccessor(); + Document.Save(vsq, vpr, ma); + + fsom.Files.Add("Project\\sequence.json", ma.ToArray()); + } + else + { + // huh? + } + objectModels.Push(fsom); + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VPR/VPRSequenceJSONDataFormat.cs b/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VPR/VPRSequenceJSONDataFormat.cs new file mode 100644 index 00000000..10c5b74a --- /dev/null +++ b/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VPR/VPRSequenceJSONDataFormat.cs @@ -0,0 +1,173 @@ +// +// VPRSequenceJSONDataFormat.cs +// +// Author: +// Michael Becker +// +// Copyright (c) 2020 Mike Becker's Software +// +// 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 UniversalEditor.DataFormats.PropertyList.JavaScriptObjectNotation; +using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized; +using UniversalEditor.ObjectModels.PropertyList; + +namespace UniversalEditor.Plugins.Vocaloid.DataFormats.Multimedia.Audio.Synthesized.Vocaloid +{ + public class VPRSequenceJSONDataFormat : JSONDataFormat + { + private static DataFormatReference _dfr = null; + protected override DataFormatReference MakeReferenceInternal() + { + if (_dfr == null) + { + _dfr = base.MakeReferenceInternal(); + _dfr.Capabilities.Add(typeof(SynthesizedAudioObjectModel), DataFormatCapabilities.All); + } + return _dfr; + } + + [CustomOptionText("Vendor", "Yamaha Corporation")] + public string Vendor { get; set; } = "Yamaha Corporation"; + + protected override void BeforeLoadInternal(Stack objectModels) + { + base.BeforeLoadInternal(objectModels); + objectModels.Push(new PropertyListObjectModel()); + } + protected override void AfterLoadInternal(Stack objectModels) + { + base.AfterLoadInternal(objectModels); + + PropertyListObjectModel plom = (objectModels.Pop() as PropertyListObjectModel); + SynthesizedAudioObjectModel vsq = (objectModels.Pop() as SynthesizedAudioObjectModel); + + Group g = (plom.Items[0] as Group); + Group gVersion = g.Items.OfType("version"); + + Property pTracks = g.Items.OfType("tracks"); + Group[] gTracks = pTracks.Value as Group[]; + if (gTracks == null) throw new InvalidDataFormatException("tracks is not a Group[]"); + + for (int i = 0; i < gTracks.Length; i++) + { + SynthesizedAudioTrack track = new SynthesizedAudioTrack(); + + Property pParts = gTracks[i].Items.OfType("parts"); + Group[] gParts = pParts.Value as Group[]; + if (gParts == null) throw new InvalidDataFormatException("parts is not a Group[]"); + + for (int j = 0; j < gParts.Length; j++) + { + Property pNotes = gParts[j].Items.OfType("notes"); + Group[] gNotes = pNotes.Value as Group[]; + if (gNotes == null) throw new InvalidDataFormatException("notes is not a Group[]"); + + for (int k = 0; k < gNotes.Length; k++) + { + SynthesizedAudioCommandNote note = new SynthesizedAudioCommandNote(); + + Property pLyric = gNotes[k].Items.OfType("lyric"); + Property pPhoneme = gNotes[k].Items.OfType("phoneme"); + Property pIsProtected = gNotes[k].Items.OfType("isProtected"); + Property pPos = gNotes[k].Items.OfType("pos"); + Property pDuration = gNotes[k].Items.OfType("duration"); + Property pNumber = gNotes[k].Items.OfType("number"); + Property pVelocity = gNotes[k].Items.OfType("velocity"); + + Group gExp = gNotes[k].Items.OfType("exp"); + Property pExpOpening = gExp.Items.OfType("opening"); + + Group dvqm = gNotes[k].Items.OfType("dvqm"); + Group dvqmRelease = dvqm.Items.OfType("release"); + Property dvqmReleaseCompID = dvqmRelease.Items.OfType("compID"); + + Group gVibrato = gNotes[k].Items.OfType("vibrato"); + Property pVibratoType = gVibrato.Items.OfType("type"); + Property pVibratoDuration = gVibrato.Items.OfType("duration"); + + track.Commands.Add(note); + } + } + + vsq.Tracks.Add(track); + } + } + protected override void BeforeSaveInternal(Stack objectModels) + { + base.BeforeSaveInternal(objectModels); + + SynthesizedAudioObjectModel vsq = (objectModels.Pop() as SynthesizedAudioObjectModel); + + PropertyListObjectModel plom = new PropertyListObjectModel(); + plom.Items.AddRange(new PropertyListItem[] + { + new Group("version", new PropertyListItem[] + { + new Property("major", 5), + new Property("minor", 0), + new Property("revision", 0) + }), + new Property("vender", Vendor), + new Property("title", vsq.Information.SongTitle), + new Group("masterTrack", new PropertyListItem[] + { + new Property("samplingRate", 44100), + new Group("loop", new PropertyListItem[] + { + new Property("isEnabled", false), + new Property("begin", 0), + new Property("end", 0) + }) + }), + new Property("voices", new Group[] + { + new Group("", new PropertyListItem[] + { + new Property("compID", "BCNFCY43LB2LZCD4"), + new Property("name", "MIKU_V4X_Original_EVEC") + }) + }), + new Property("tracks", new Group[] + { + new Group("", new PropertyListItem[] + { + new Property("type", 0), + new Property("name", "MIKU_V4X_ORIGINAL_EVEC"), + new Property("color", 0), + new Property("busNo", 0), + new Property("isFolded", false), + new Property("height", 0.0), + new Group("volume", new PropertyListItem[] + { + new Property("isFolded", true), + new Property("height", 0.0), + new Property("events", new Group[] + { + new Group("", new PropertyListItem[] + { + new Property("pos" , 0.0), + new Property("value", 0.0) + }) + }) + }) + }) + }) + }); + objectModels.Push(plom); + } + } +} diff --git a/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VSQDataFormat.cs b/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VSQDataFormat.cs index 944b2864..4d3989a9 100644 --- a/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VSQDataFormat.cs +++ b/Plugins/UniversalEditor.Plugins.Vocaloid/DataFormats/Multimedia/Audio/Synthesized/Vocaloid/VSQDataFormat.cs @@ -45,7 +45,7 @@ namespace UniversalEditor.Plugins.Vocaloid.DataFormats.Multimedia.Audio.Synthesi protected override void AfterLoadInternal(Stack objectModels) { SynthesizedAudioObjectModel audio = objectModels.Pop() as SynthesizedAudioObjectModel; - for (int i = 1; i < audio.Tracks.Count; i++) + for (int i = 0; i < audio.Tracks.Count; i++) { SynthesizedAudioTrack track = audio.Tracks[i]; string text = string.Empty; @@ -108,7 +108,7 @@ namespace UniversalEditor.Plugins.Vocaloid.DataFormats.Multimedia.Audio.Synthesi note.Phoneme = phonemeInfos[1]; note.Length = length; note.Frequency = 128 - noteNumber; - track.Commands.Add(note); + note.Position = thisNotePosition; if (grp.Items.IndexOf(prop) < grp.Items.Count - 1) { int nextNotePosition = int.Parse(grp.Items[grp.Items.IndexOf(prop) + 1].Name); @@ -120,6 +120,7 @@ namespace UniversalEditor.Plugins.Vocaloid.DataFormats.Multimedia.Audio.Synthesi track.Commands.Add(rest); } } + track.Commands.Add(note); break; } case "Singer": diff --git a/Plugins/UniversalEditor.Plugins.Vocaloid/UniversalEditor.Plugins.Vocaloid.csproj b/Plugins/UniversalEditor.Plugins.Vocaloid/UniversalEditor.Plugins.Vocaloid.csproj index e84e12ee..3cbe2378 100644 --- a/Plugins/UniversalEditor.Plugins.Vocaloid/UniversalEditor.Plugins.Vocaloid.csproj +++ b/Plugins/UniversalEditor.Plugins.Vocaloid/UniversalEditor.Plugins.Vocaloid.csproj @@ -37,6 +37,8 @@ + + @@ -47,6 +49,7 @@ + @@ -61,6 +64,10 @@ {BE4D0BA3-0888-42A5-9C09-FC308A4509D2} UniversalEditor.Plugins.Multimedia + + {76FD1306-9CA4-428F-993B-B7E4EEEACBF3} + UniversalEditor.Plugins.FileSystem + \ No newline at end of file