using System; using System.Linq; 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.Dialog { /// <summary> /// 简化版听音界面(不带实时波形图) /// </summary> public partial class SimplyAuditionDialog : UserControl { private readonly AudioVisualizer _visualizer; // 可视化 private double[] _spectrumData; // 频谱数据 private readonly WasapiCapture _capture; // 音频捕获 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 SimplyAuditionDialog(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 SimplyAuditionDialog_OnLoaded(object sender, RoutedEventArgs e) { _capture.StartRecording(); _dataTimer.Start(); _drawingTimer.Start(); } private void SimplyAuditionDialog_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]; var bassArea = AudioDataHub.Get.TakeSpectrumOfFrequency( _spectrumData, RuntimeCache.AudioSampleRate, 250 ); var bassScale = bassArea.Average() * 100; //低音区 //波形曲线 var curveBrush = new SolidColorBrush(color1); DrawCurve( SampleWavePath, curveBrush, _visualizer.SampleData, _visualizer.SampleData.Length, SampleWavePanel.ActualWidth, 0, SampleWavePanel.ActualHeight / 2, Math.Min(SampleWavePanel.ActualHeight / 2, 50) ); //四周边框 DrawGradientBorder( TopBorder, BottomBorder, LeftBorder, RightBorder, Color.FromArgb(0, color1.R, color1.G, color1.B), color2, SampleWavePanel.ActualWidth / 3, bassScale ); } /// <summary> /// 画曲线 /// </summary> /// <param name="wavePath"></param> /// <param name="brush"></param> /// <param name="spectrumData"></param> /// <param name="pointCount"></param> /// <param name="drawingWidth"></param> /// <param name="xOffset"></param> /// <param name="yOffset"></param> /// <param name="scale">控制波形图波峰高度和波谷深度</param> private void DrawCurve( Path wavePath, Brush brush, double[] spectrumData, int pointCount, double drawingWidth, double xOffset, double yOffset, double scale ) { var pointArray = new Point[pointCount]; for (var i = 0; i < pointCount; i++) { var x = i * drawingWidth / pointCount + xOffset; var y = spectrumData[i * spectrumData.Length / pointCount] * scale + yOffset; pointArray[i] = new Point(x, y); } var figure = new PathFigure { StartPoint = pointArray[0] }; figure.Segments.Add(new PolyLineSegment(pointArray, true)); wavePath.Data = new PathGeometry { Figures = { figure } }; wavePath.Stroke = brush; } /// <summary> /// 画四周渐变边框 /// </summary> /// <param name="topBorder"></param> /// <param name="bottomBorder"></param> /// <param name="leftBorder"></param> /// <param name="rightBorder"></param> /// <param name="inner"></param> /// <param name="outer"></param> /// <param name="width">画图宽度</param> /// <param name="bassScale">高低音转化比例</param> private void DrawGradientBorder( Rectangle topBorder, Rectangle bottomBorder, Rectangle leftBorder, Rectangle rightBorder, Color inner, Color outer, double width, double bassScale ) { //边框粗细根据音频高低音变化 var thickness = (int)(width * bassScale); topBorder.Height = thickness; bottomBorder.Height = thickness; leftBorder.Width = thickness; rightBorder.Width = thickness; topBorder.Fill = new LinearGradientBrush(outer, inner, 90); bottomBorder.Fill = new LinearGradientBrush(inner, outer, 90); leftBorder.Fill = new LinearGradientBrush(outer, inner, 0); rightBorder.Fill = new LinearGradientBrush(inner, outer, 0); } } }