using SongVocalSectionAnalyser.Common.Collections; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; namespace SongVocalIsolateAutomation.UI { internal partial class WaveDubGraphic : UserControl { private WaveDub _waveDub; private bool _updateBitmap; private Bitmap _bufferedBitmap; private float _zoomRate = 1; private int _stageWidth, _stageHeight, _stageWidthSamples, _cursorX; public WaveDubGraphic() { InitializeComponent(); } public void SetData(WaveDub waveDub) { _waveDub = waveDub; if (null != _waveDub) { BottomScrollBar.Maximum = _waveDub.Length; } ReDraw(); } //----------------------------- Private method -------------------- private void ReDraw() { _updateBitmap = true; GraphControl.Invalidate(); } private void UpdateScaleData() { _stageWidth = GraphControl.Width; _stageHeight = GraphControl.Height; _stageWidthSamples = (int)(_stageWidth / _zoomRate) + 1; BottomScrollBar.LargeChange = _stageWidthSamples; } //----------------------------- EVT -------------------- private void GraphControl_Resize(object sender, EventArgs e) { var old = _bufferedBitmap; _bufferedBitmap = new Bitmap(GraphControl.Width, GraphControl.Height, PixelFormat.Format32bppArgb); old?.Dispose(); UpdateScaleData(); ReDraw(); } private void GraphControl_Paint(object sender, PaintEventArgs e) { e.Graphics.SetHighQuility(); if (_updateBitmap) { try { Render(); } catch (Exception exception) { e.Graphics.DrawString(exception.ToString(), Font, Brushes.Red, Point.Empty); } _updateBitmap = false; } e.Graphics.DrawImage(_bufferedBitmap, Point.Empty); } private void BottomScrollBar_Scroll(object sender, ScrollEventArgs e) { ReDraw(); } private void TopScrollBar_Scroll(object sender, ScrollEventArgs e) { _waveDub.OffsetI = TopScrollBar.Value; ReDraw(); } private void GraphControl_MouseMove(object sender, MouseEventArgs e) { _cursorX = e.X; ReDraw(); } private void ScalePot_ValueChanged(object sender, EventArgs e) { _zoomRate = ScalePot.Value < 1 ? (float)ScalePot.Value : (float)(1 + (ScalePot.Value - 1) * 20); UpdateScaleData(); ReDraw(); } private void Render() { var iColor = Color.FromArgb(224, Color.Red); var vColor = Color.FromArgb(224, Color.Blue); var cColor = Color.FromArgb(224, Color.Green); var wColor = Color.FromArgb(224, Color.Yellow); using (var vPen = new Pen(vColor)) using (var iPen = new Pen(iColor)) using (var cPen = new Pen(cColor, 2)) using (var vBrush = new SolidBrush(vColor)) using (var iBrush = new SolidBrush(iColor)) using (var cBrush = new SolidBrush(cColor)) using (var wBrush = new SolidBrush(wColor)) using (var g = Graphics.FromImage(_bufferedBitmap)) { g.SetHighQuility(); g.Clear(Color.Transparent); var halfStageHeight = _stageHeight / 2.0f; //middle zero line g.FillRectangle(wBrush, 0, halfStageHeight - halfStageHeight * 0.2f, _stageWidth, halfStageHeight * 0.4f); g.DrawLine(Pens.Black, 0, halfStageHeight, _stageWidth, halfStageHeight); g.DrawLine(Pens.Black, _cursorX, 0, _cursorX, _stageHeight); if (null == _waveDub) return; var yRate = halfStageHeight; if (_zoomRate < 1) { //compact vertical line float iSampleIndex = BottomScrollBar.Value + _waveDub.OffsetI; float vSampleIndex = BottomScrollBar.Value; var pixelPerSample = 1 / _zoomRate; for (var x = 0; x < _stageWidth && vSampleIndex < _waveDub.SamplesV.Count; x++) { var skip = pixelPerSample * x; var vChunk = _waveDub.SamplesV.SkipFast((int)(vSampleIndex + skip)).Take((int)pixelPerSample).ToArray(); if (0 == vChunk.Length) break; var vMin = vChunk.Min(); var vMax = vChunk.Max(); var xMidPoint = new PointF(x, halfStageHeight); var yPoint = new PointF(x, vMin * yRate + halfStageHeight); g.DrawLine(vPen, xMidPoint, yPoint); yPoint.Y = vMax * yRate + halfStageHeight; g.DrawLine(vPen, xMidPoint, yPoint); var iChunk = _waveDub.SamplesI.SkipFast((int)(vSampleIndex + skip)).Take((int)pixelPerSample).ToArray(); if (0 != iChunk.Length) { var iMax = iChunk.Max(); var iMin = iChunk.Min(); yPoint.Y = iMin * yRate + halfStageHeight; g.DrawLine(iPen, xMidPoint, yPoint); yPoint.Y = iMax * yRate + halfStageHeight; g.DrawLine(iPen, xMidPoint, yPoint); var cChunk = vChunk.Zip(iChunk, (v, i) => v - i).ToArray(); var cMax = cChunk.Max(); var cMin = cChunk.Min(); yPoint.Y = cMin * yRate + halfStageHeight; g.DrawLine(cPen, xMidPoint, yPoint); yPoint.Y = cMax * yRate + halfStageHeight; g.DrawLine(cPen, xMidPoint, yPoint); } vSampleIndex += pixelPerSample; iSampleIndex += pixelPerSample; } } else { var iPoints = new List(_stageWidthSamples); var vPoints = new List(_stageWidthSamples); var cPoints = new List(_stageWidthSamples); var iSampleIndex = BottomScrollBar.Value + _waveDub.OffsetI; var vSampleIndex = BottomScrollBar.Value; for (var x = 0; x < _stageWidthSamples && vSampleIndex < _waveDub.SamplesV.Count; x++) { var vValue = _waveDub.SamplesV[vSampleIndex]; var scaledX = x * _zoomRate; vPoints.Add(new PointF(scaledX, vValue * yRate + halfStageHeight)); if (iSampleIndex >= 0 && iSampleIndex < _waveDub.SamplesI.Count) { var iValue = _waveDub.SamplesI[iSampleIndex]; iPoints.Add(new PointF(scaledX, iValue * yRate + halfStageHeight)); var cValue = vValue - iValue; cPoints.Add(new PointF(scaledX, cValue * yRate + halfStageHeight)); } vSampleIndex++; iSampleIndex++; } if (vPoints.Count > 1) g.DrawLines(vPen, vPoints.ToArray()); if (iPoints.Count > 1) g.DrawLines(iPen, iPoints.ToArray()); if (cPoints.Count > 1) g.DrawLines(cPen, cPoints.ToArray()); if (_zoomRate >= 10) { var dotRc = new RectangleF(0, 0, 6, 6); //show point in sample foreach (var vPoint in vPoints) { dotRc.Location = vPoint; dotRc.Offset(-3, -3); g.FillEllipse(vBrush, dotRc); } foreach (var iPoint in iPoints) { dotRc.Location = iPoint; dotRc.Offset(-3, -3); g.FillEllipse(iBrush, dotRc); } foreach (var cPoint in cPoints) { dotRc.Location = cPoint; dotRc.Offset(-3, -3); g.FillEllipse(cBrush, dotRc); } } } } } } }