preliminary implementation of a linear Waveform audio editor similar to Audacity

This commit is contained in:
Michael Becker 2020-05-24 10:21:29 -04:00
parent 781e7597df
commit e34df72fc9
No known key found for this signature in database
GPG Key ID: 506F54899E2BFED7
8 changed files with 686 additions and 0 deletions

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow">
<property name="can_focus">False</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkDrawingArea" id="ctTrackControlPanel">
<property name="name">UniversalEditor.Plugins.Multimedia.UserInterface.Editors.Multimedia.Audio.Waveform.Controls.WaveformAudioEditorTrackControlPanel</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkDrawingArea" id="ctTrackWaveform">
<property name="name">UniversalEditor.Plugins.Multimedia.UserInterface.Editors.Multimedia.Audio.Waveform.Controls.WaveformAudioEditorTrackWaveform</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="upper">100</property>
<property name="value">75</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
<property name="page_size">10</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">100</property>
<property name="value">50</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-missing-image</property>
</object>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-go-up</property>
</object>
<object class="GtkWindow">
<property name="can_focus">False</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="cmdDelete">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">image1</property>
<property name="relief">none</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="txtTrackName">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="cmdMenu">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="focus_on_click">False</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="ctButtons">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkToggleButton" id="cmdMute">
<property name="label" translatable="yes">Mute</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="cmdSolo">
<property name="label" translatable="yes">Solo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="ctSliders">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="label" translatable="yes">+</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="label" translatable="yes">-</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="label" translatable="yes">R</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="label" translatable="yes">L</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkScale" id="sldVolume">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="adjustment">adjustment1</property>
<property name="lower_stepper_sensitivity">off</property>
<property name="upper_stepper_sensitivity">off</property>
<property name="round_digits">1</property>
<property name="draw_value">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkScale" id="sldPan">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="adjustment">adjustment2</property>
<property name="lower_stepper_sensitivity">off</property>
<property name="upper_stepper_sensitivity">off</property>
<property name="draw_value">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lblBitrate">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="label" translatable="yes">Mono, 22050 Hz</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lblFormat">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="label" translatable="yes">32-bit float</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="cmdExpandCollapse">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">image2</property>
<property name="relief">none</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cmdSelect">
<property name="label" translatable="yes">Select</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -737,6 +737,8 @@
<Content Include="Editors\Executable\ExecutableEditor.glade" />
<Content Include="Editors\Multimedia\Audio\Synthesized\MenuBar.uexml" />
<Content Include="Editors\Multimedia\Audio\Synthesized\Toolbox.uexml" />
<Content Include="Editors\Multimedia\Audio\Waveform\Controls\WaveformAudioEditorTrackControlPanel.glade" />
<Content Include="Editors\Multimedia\Audio\Waveform\Controls\WaveformAudioEditorTrack.glade" />
<Content Include="Editors\Multimedia\Audio\Synthesized\Views\MIDIEvents\MIDIEventsView.glade" />
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,49 @@
//
// WaveformAudioEditorTrack.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using MBS.Framework.UserInterface;
using UniversalEditor.ObjectModels.Multimedia.Audio.Waveform;
namespace UniversalEditor.Plugins.Multimedia.UserInterface.Editors.Multimedia.Audio.Waveform.Controls
{
[ContainerLayout("~/Editors/Multimedia/Audio/Waveform/Controls/WaveformAudioEditorTrack.glade")]
public class WaveformAudioEditorTrack : Container
{
private WaveformAudioEditorTrackControlPanel ctTrackControlPanel;
private WaveformAudioEditorTrackWaveform ctTrackWaveform;
public WaveformAudioObjectModel ObjectModel { get; set; }
public WaveformAudioEditorTrack()
{
}
public WaveformAudioEditorTrack(WaveformAudioObjectModel wave)
{
ObjectModel = wave;
}
protected override void OnCreated(EventArgs e)
{
base.OnCreated(e);
}
}
}

View File

@ -0,0 +1,66 @@
//
// WaveformAudioEditorTrackControlPanel.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using MBS.Framework.UserInterface;
using MBS.Framework.UserInterface.Controls;
namespace UniversalEditor.Plugins.Multimedia.UserInterface.Editors.Multimedia.Audio.Waveform.Controls
{
[ContainerLayout("~/Editors/Multimedia/Audio/Waveform/Controls/WaveformAudioEditorTrackControlPanel.glade")]
public class WaveformAudioEditorTrackControlPanel : Container
{
private Button cmdDelete;
private TextBox txtTrackName;
private Button cmdMenu;
private Container ctButtons;
private Button cmdMute;
private Button cmdSolo;
private Container ctSliders;
private Label lblBitrate;
private Label lblFormat;
private Button cmdExpandCollapse;
private Button cmdSelect;
protected override void OnCreated(EventArgs e)
{
base.OnCreated(e);
}
private void cmdExpandCollapse_Click(object sender, EventArgs e)
{
if (ctButtons.Visible)
{
ctButtons.Visible = false;
ctSliders.Visible = false;
lblBitrate.Visible = false;
lblFormat.Visible = false;
}
else
{
ctButtons.Visible = true;
ctSliders.Visible = true;
lblBitrate.Visible = true;
lblFormat.Visible = true;
}
}
}
}

View File

@ -0,0 +1,138 @@
//
// WaveformAudioEditorTrackWaveform.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using MBS.Framework.Drawing;
using MBS.Framework.UserInterface;
using MBS.Framework.UserInterface.Drawing;
using MBS.Framework.UserInterface.Input.Mouse;
using UniversalEditor.ObjectModels.Multimedia.Audio.Waveform;
namespace UniversalEditor.Plugins.Multimedia.UserInterface.Editors.Multimedia.Audio.Waveform.Controls
{
public class WaveformAudioEditorTrackWaveform : CustomControl
{
protected override void OnCreated(EventArgs e)
{
base.OnCreated(e);
ScrollBounds = new MBS.Framework.Drawing.Dimension2D(4096, 0);
Cursor = Cursors.Text;
}
private static bool preventScroll = false;
private int _SelectionStart = 0;
public int SelectionStart { get { return _SelectionStart; } set { _SelectionStart = value; Refresh(); } }
private int _SelectionLength = 0;
public int SelectionLength { get { return _SelectionLength; } set { _SelectionLength = value; Refresh(); } }
public double ScaleFactorY { get; set; } = 0.5;// 1.0;
private int origStart = 0;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
origStart = (int)e.X;
_SelectionStart = (int)e.X;
_SelectionLength = 0;
Refresh();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Buttons == MouseButtons.Primary)
{
if ((int)(e.X - origStart) < 0)
{
_SelectionStart = origStart + (int)(e.X - origStart);
_SelectionLength = (int)(origStart - e.X);
}
else
{
_SelectionStart = origStart;
_SelectionLength = (int)(e.X - _SelectionStart);
}
Refresh();
}
}
private const int WAVEFORM_MIDPOINT = 128;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
WaveformAudioEditorTrack track = (Parent as WaveformAudioEditorTrack);
WaveformAudioObjectModel wave = track.ObjectModel;
ScrollBounds = new MBS.Framework.Drawing.Dimension2D(wave.RawSamples.Length, 0);
Vector2D lastPoint = new Vector2D(0, WAVEFORM_MIDPOINT);
Pen pen = new Pen(SystemColors.HighlightBackground);
Pen pen2 = new Pen(SystemColors.HighlightForeground);
if (SelectionLength > 0)
{
e.Graphics.FillRectangle(new SolidBrush(SystemColors.HighlightBackground), new Rectangle(SelectionStart, 0, SelectionLength, Size.Height));
}
e.Graphics.DrawLine(new Pen(Colors.Black), 0, WAVEFORM_MIDPOINT * ScaleFactorY, HorizontalAdjustment.Value + Size.Width, WAVEFORM_MIDPOINT * ScaleFactorY);
e.Graphics.DrawLine(new Pen(Colors.White), SelectionStart, 0, SelectionStart, Size.Height);
for (int i = 0; i < wave.RawSamples.Length; i++)
{
double x = i;
double y = (ScaleFactorY * wave.RawSamples[i]);
if (x >= SelectionStart && x <= (SelectionStart + SelectionLength))
{
e.Graphics.DrawLine(pen2, lastPoint.X, lastPoint.Y, x, y);
}
else
{
e.Graphics.DrawLine(pen, lastPoint.X, lastPoint.Y, x, y);
}
lastPoint = new Vector2D(x, y);
}
}
protected override void OnScrolled(ScrolledEventArgs e)
{
if (preventScroll) return;
preventScroll = true;
WaveformAudioEditor ed = (Parent.Parent as WaveformAudioEditor);
for (int i = 0; i < ed.Controls.Count; i++)
{
if (ed.Controls[i] is WaveformAudioEditorTrack)
{
WaveformAudioEditorTrackWaveform wvff = ((WaveformAudioEditorTrack)ed.Controls[i]).Controls[1] as WaveformAudioEditorTrackWaveform;
wvff.HorizontalAdjustment.Value = HorizontalAdjustment.Value;
}
}
preventScroll = false;
base.OnScrolled(e);
}
}
}

View File

@ -0,0 +1,74 @@
//
// WaveformAudioEditor.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using MBS.Framework.UserInterface;
using MBS.Framework.UserInterface.Layouts;
using UniversalEditor.ObjectModels.Multimedia.Audio.Waveform;
using UniversalEditor.Plugins.Multimedia.UserInterface.Editors.Multimedia.Audio.Waveform.Controls;
using UniversalEditor.UserInterface;
namespace UniversalEditor.Plugins.Multimedia.UserInterface.Editors.Multimedia.Audio.Waveform
{
public class WaveformAudioEditor : Editor
{
private static EditorReference _er = null;
public override EditorReference MakeReference()
{
if (_er == null)
{
_er = base.MakeReference();
_er.SupportedObjectModels.Add(typeof(WaveformAudioObjectModel));
}
return _er;
}
public WaveformAudioEditor()
{
Layout = new BoxLayout(Orientation.Vertical);
}
protected override void OnObjectModelChanged(EventArgs e)
{
base.OnObjectModelChanged(e);
this.Controls.Clear();
WaveformAudioObjectModel wave = (ObjectModel as WaveformAudioObjectModel);
if (wave == null) return;
BoxLayout.Constraints cc = new BoxLayout.Constraints(false, false);
cc.VerticalExpand = false;
cc.HorizontalExpand = true;
this.Controls.Add(new WaveformAudioEditorTrack(wave), cc);
}
public override void UpdateSelections()
{
throw new System.NotImplementedException();
}
protected override EditorSelection CreateSelectionInternal(object content)
{
throw new System.NotImplementedException();
}
}
}

View File

@ -50,6 +50,10 @@
<Compile Include="Editors\Multimedia\Audio\Synthesized\Dialogs\NotePropertiesDialog.cs" />
<Compile Include="Editors\Multimedia\Audio\Synthesized\Views\PianoRoll\PianoRollView.cs" />
<Compile Include="Editors\Multimedia\Audio\Synthesized\Views\PianoRoll\PianoRollViewSelectionMode.cs" />
<Compile Include="Editors\Multimedia\Audio\Waveform\WaveformAudioEditor.cs" />
<Compile Include="Editors\Multimedia\Audio\Waveform\Controls\WaveformAudioEditorTrackControlPanel.cs" />
<Compile Include="Editors\Multimedia\Audio\Waveform\Controls\WaveformAudioEditorTrack.cs" />
<Compile Include="Editors\Multimedia\Audio\Waveform\Controls\WaveformAudioEditorTrackWaveform.cs" />
<Compile Include="Editors\Multimedia\Audio\Synthesized\Views\MIDIEvents\MIDIEventsView.cs" />
</ItemGroup>
<ItemGroup>
@ -68,6 +72,8 @@
<Folder Include="Editors\Multimedia\Audio\Synthesized\Dialogs\" />
<Folder Include="Editors\Multimedia\Audio\Synthesized\Views\" />
<Folder Include="Editors\Multimedia\Audio\Synthesized\Views\PianoRoll\" />
<Folder Include="Editors\Multimedia\Audio\Waveform\" />
<Folder Include="Editors\Multimedia\Audio\Waveform\Controls\" />
<Folder Include="Editors\Multimedia\Audio\Synthesized\Views\MIDIEvents\" />
</ItemGroup>
<ItemGroup>