using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; using Correlator.DataService; using Correlator.Util; using NAudio.CoreAudioApi; using NAudio.Wave; namespace Correlator.Views { public partial class AudioFileView : UserControl { private readonly AudioVisualizer _visualizer; // 可视化 private readonly WasapiCapture _capture; // 音频捕获 private double[] _spectrumData; // 频谱数据 private int _colorIndex; private readonly Color[] _allColors; private readonly DispatcherTimer _dataTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 30) }; private readonly DispatcherTimer _drawingTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 30) }; public AudioFileView(IApplicationDataService dataService) { InitializeComponent(); _capture = new WasapiLoopbackCapture(); // 捕获电脑发出的声音 _visualizer = dataService.GetAudioVisualizer(512); _allColors = dataService.GetAllHsvColors(); // 获取所有的渐变颜色 (HSV 颜色) _capture.WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(7500, 1); _capture.DataAvailable += delegate(object o, WaveInEventArgs args) { var length = args.BytesRecorded / 4; // 采样的数量 (每一个采样是 4 字节) var result = new double[length]; for (var i = 0; i < length; i++) { result[i] = BitConverter.ToSingle(args.Buffer, i * 4); // 取出采样值 } _visualizer.PushSampleData(result); // 将新的采样存储到 可视化器 中 }; _dataTimer.Tick += DataTimer_Tick; _drawingTimer.Tick += DrawingTimer_Tick; } private void AudioFileView_OnLoaded(object sender, RoutedEventArgs e) { _capture.StartRecording(); _dataTimer.Start(); _drawingTimer.Start(); } private void AudioFileView_OnUnloaded(object sender, RoutedEventArgs e) { _drawingTimer.Stop(); _dataTimer.Stop(); _capture.StopRecording(); } /// <summary> /// 刷新频谱数据以及实现频谱数据缓动 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DataTimer_Tick(object sender, EventArgs e) { var newSpectrumData = _visualizer.GetSpectrumData(); // 从可视化器中获取频谱数据 newSpectrumData = AudioDataHub.Get.MakeSmooth(newSpectrumData, 2); // 平滑频谱数据 if (_spectrumData == null) // 如果已经存储的频谱数据为空, 则把新的频谱数据直接赋值上去 { _spectrumData = newSpectrumData; return; } for (var i = 0; i < newSpectrumData.Length; i++) // 计算旧频谱数据和新频谱数据之间的 "中间值" { var oldData = _spectrumData[i]; var newData = newSpectrumData[i]; // 每一次执行, 频谱值会向目标值移动 20% (如果太大, 缓动效果不明显, 如果太小, 频谱会有延迟的感觉) var lerpData = oldData + (newData - oldData) * .2f; _spectrumData[i] = lerpData; } } private void DrawingTimer_Tick(object sender, EventArgs e) { if (_spectrumData == null) { return; } _colorIndex++; var color1 = _allColors[_colorIndex % _allColors.Length]; var color2 = _allColors[(_colorIndex + 200) % _allColors.Length]; DrawGradientStrips( StripsPath, color1, color2, _spectrumData, _spectrumData.Length, StripsPath.ActualWidth, 0, StripsPath.ActualHeight, 2, -StripsPath.ActualHeight * 200 ); } /// <summary> /// 绘制渐变的条形 /// </summary> /// <param name="stripsPath">绘图目标</param> /// <param name="bottomColor">下方颜色</param> /// <param name="topColor">上方颜色</param> /// <param name="spectrumData">频谱数据</param> /// <param name="stripCount">条形的数量</param> /// <param name="drawingWidth">绘图的宽度</param> /// <param name="xOffset">绘图的起始 X 坐标</param> /// <param name="yOffset">绘图的起始 Y 坐标</param> /// <param name="spacing">条形与条形之间的间隔(像素)</param> /// <param name="scale">控制波形图波峰高度</param> private void DrawGradientStrips( Path stripsPath, Color bottomColor, Color topColor, double[] spectrumData, int stripCount, double drawingWidth, double xOffset, double yOffset, double spacing, double scale ) { //竖条宽度 var stripWidth = (drawingWidth - spacing * stripCount) / stripCount; var pointArray = new Point[stripCount]; for (var i = 0; i < stripCount; i++) { var x = stripWidth * i + spacing * i + xOffset; var y = spectrumData[i * spectrumData.Length / stripCount] * scale; // height //给所有频谱位置赋值 pointArray[i] = new Point(x, y); } //生成一系列频谱竖条 var geometry = new GeometryGroup(); foreach (var point in pointArray) { var height = point.Y; if (height < 0) { height = -height; } //每根竖条的四个角坐标 var endPoints = new[] { new Point(point.X, point.Y + yOffset), //左下角 new Point(point.X, point.Y + height + yOffset), //左上角 new Point(point.X + stripWidth, point.Y + height + yOffset), //右上角 new Point(point.X + stripWidth, point.Y + yOffset) //右下角 }; var figure = new PathFigure { StartPoint = endPoints[0] }; figure.Segments.Add(new PolyLineSegment(endPoints, false)); geometry.Children.Add(new PathGeometry { Figures = { figure } }); } stripsPath.Data = geometry; //设置频谱竖条的渐变色 var linearGradientBrush = new LinearGradientBrush( bottomColor, topColor, new Point(0, 0), new Point(0, 1) ); stripsPath.Fill = linearGradientBrush; } } }