public DrawHandler(WindowGraphics windowGraphics) { this.windowGraphics = windowGraphics; windowGraphics.TryUpdateGraphics(); DrawingContext = windowGraphics.drawingVisual.RenderOpen(); var transformGroup = new TransformGroup(); transformGroup.Children.Add(new ScaleTransform(1, -1)); transformGroup.Children.Add(new TranslateTransform(0, windowGraphics.height)); transformGroup.Freeze(); DrawingContext.PushTransform(transformGroup); }
/// <summary> /// 绘制波形 /// </summary> /// <param name="win"></param> /// <param name="waveQueueData"></param> /// <param name="polylineStyle"></param> /// <param name="id">区分线程,调试用</param> static void DrawWave(Window win, FixedQueueArray <float> waveQueueData, bool polylineStyle, int id = 0) { var lineColor = Color.FromRgb(17, 125, 187); var lineBrush = new SolidColorBrush(lineColor); lineBrush.Freeze(); var pen = new Pen(lineBrush, 1); pen.Freeze(); // WindowGraphics 这是一个可以跨进程绘制窗体的类 // 使用 WPF 中的 DrawingContext 进行绘制 var wg = new WindowGraphics(win); while (true) { Task.Delay(20).Wait(); using (var drawCtx = wg.OpenDraw()) { Span <float> waveData = new float[(int)drawCtx.Size.Width]; waveQueueData.Read(waveData); drawCtx.DrawingContext.DrawRectangle(Brushes.White, null, new Rect(drawCtx.Size)); DrawBorder(drawCtx); var h2 = drawCtx.Size.Height / 2; Point[] points; if (polylineStyle) { const int step = 10; points = new Point[waveData.Length / step + 1]; for (int i = 0; i < points.Length; i++) { double sum = 0; int count = 0; for (int j = 0; j < step; j++) { if (i * step + j >= waveData.Length) { break; } sum += waveData[i * step + j]; count++; } var avg = count > 0 ? sum / count : 0; points[i] = new Point(i * step, h2 + avg * h2); } points[points.Length - 1].X = drawCtx.Size.Width; } else { points = new Point[waveData.Length]; for (int i = 0; i < waveData.Length; i++) { double y = waveData[i] * h2; points[i] = new Point(i, h2 + y); } } var geometry = new StreamGeometry(); using (var geometryCtx = geometry.Open()) { geometryCtx.BeginFigure(points[0], false, false); geometryCtx.PolyLineTo(points, true, true); } geometry.Freeze(); drawCtx.DrawingContext.DrawGeometry(null, pen, geometry); } } }
/// <summary> /// 绘制频谱(FFT算法用的是FFTW库) /// </summary> /// <param name="win"></param> /// <param name="nextData"></param> /// <param name="sampleRate"></param> /// <param name="polylineStyle"></param> /// <param name="id">区分线程,调试用</param> static void DrawSpectrum(Window win, float[] nextData, int sampleRate, bool polylineStyle, int id = 0) { double * inBuff = (double *)fftw.alloc_complex((IntPtr)FFTSize); double * outBuff = (double *)fftw.alloc_complex((IntPtr)FFTSize); IntPtr fft = fftw.dft_1d(FFTSize, (IntPtr)inBuff, (IntPtr)outBuff, fftw_direction.Forward, fftw_flags.Estimate); Span <double> tempBuff = stackalloc double[FFTSize]; var cutLength = FFTTools.CutFrequencyLength(FFTSize, MinimumFrequency, MaximumFrequency, sampleRate, FFTComplexCount); Span <double> cutData = tempBuff.Slice(0, cutLength); DSP.Window fftWindow = new DSP.BlackmanNuttallWindow(FFTSize); ILogarithm log = new DSP.Decade(); var lineBrush = new SolidColorBrush(Color.FromRgb(17, 125, 187)); var pen = new Pen(lineBrush, 1); var backBrush = new SolidColorBrush(Color.FromRgb(241, 246, 250)); lineBrush.Freeze(); pen.Freeze(); backBrush.Freeze(); Point[] drawPoints = null; Span <double> logData = null; var wg = new WindowGraphics(win); while (true) { Task.Delay(20).Wait(); using (var drawCtx = wg.OpenDraw()) { lock (fftLock) { for (int i = 0; i < nextData.Length; i++) { tempBuff[i] = nextData[i]; } } // 加窗 fftWindow?.Apply(tempBuff, tempBuff); for (int i = 0; i < tempBuff.Length; i++) { inBuff[2 * i + 0] = tempBuff[i]; inBuff[2 * i + 1] = 0; } // 执行FFT fftw.execute(fft); if (logData == null || logData.Length != (int)drawCtx.Size.Width) { logData = new double[(int)drawCtx.Size.Width]; } double h = drawCtx.Size.Height; // FFT复数结果取模 FFTTools.Abs(FFTSize, new ReadOnlySpan <Complex>(outBuff, FFTSize), tempBuff, true, fftWindow); // 保留感兴趣的频率区域 FFTTools.CutFrequency(FFTSize, MinimumFrequency, MaximumFrequency, sampleRate, tempBuff, cutData); // 频率以对数方式显示 FFTTools.Logarithm(cutData, MinimumFrequency, MaximumFrequency, logData, log); // 振幅以分贝方式显示 FFTTools.ToDB(logData, MaxDB); // 因为上面的结果在区间[0,1],所以乘以窗体高度h,才能画满整个窗体 FFTTools.Scale(logData, h); if (polylineStyle) { const int step = 10; var points = new Point[logData.Length / step + 1]; for (int i = 0; i < points.Length; i++) { double max = 0; for (int j = 0; j < step; j++) { if (i * step + j >= logData.Length) { break; } max = Math.Max(logData[i * step + j], max); } points[i] = new Point(i * step, h - max); } points[points.Length - 1].X = drawCtx.Size.Width; drawPoints = new Point[points.Length + 2]; points.AsSpan().CopyTo(drawPoints); } else { drawPoints = new Point[logData.Length + 2]; for (int i = 0; i < logData.Length; i++) { drawPoints[i] = new Point(i, h - logData[i]); } } drawPoints[drawPoints.Length - 2] = new Point(drawCtx.Size.Width, drawCtx.Size.Height); drawPoints[drawPoints.Length - 1] = new Point(0, drawCtx.Size.Height); var geometry = new StreamGeometry(); using (var geometryCtx = geometry.Open()) { geometryCtx.BeginFigure(drawPoints[0], true, true); geometryCtx.PolyLineTo(drawPoints, true, true); } geometry.Freeze(); drawCtx.DrawingContext.DrawRectangle(Brushes.White, null, new Rect(drawCtx.Size)); drawCtx.DrawingContext.DrawGeometry(backBrush, null, geometry); DrawBorder(drawCtx); drawCtx.DrawingContext.DrawGeometry(null, pen, geometry); } } }