C# 获取系统声卡音频数据,并绘制波形
//by wgscd //date:2022/11/7
UI:
<Path Stroke="Red" Data="{Binding path}" RenderTransformOrigin="0.5,0.5"> <Path.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="180"/> <TranslateTransform/> </TransformGroup> </Path.RenderTransform> </Path>
Code:
using NAudio.CoreAudioApi; using NAudio.Wave.SampleProviders; using NAudio.Wave; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using NAudio.Wave.Compression; using System.Runtime.Remoting.Channels; using System.ComponentModel; using System.Collections.ObjectModel; using HandyControl.Collections; using System.Threading; using System.Windows.Threading; using System.Windows.Media; using System.Windows.Shapes; using System.Windows; using System.CodeDom; namespace DouyuDanmu { //by wgscd //date:2022/11/7 public class NAudioHelper { public static SampleData pathData = new SampleData(); public static void GetSampleData() { // Redefine the capturer instance with a new instance of the LoopbackCapture class WasapiLoopbackCapture _waveIn = new WasapiLoopbackCapture(); _waveIn.WaveFormat = new WaveFormat(16000, 16, 2); // Redefine the audio writer instance with the given configuration //WaveFileWriter RecordedAudioWriter = new WaveFileWriter(outputFilePath, CaptureInstance.WaveFormat); // When the capturer receives audio, start writing the buffer into the mentioned file _waveIn.DataAvailable += (s, a) => { // Write buffer into the file of the writer instance AnalyzeVoice(a.Buffer); }; _waveIn.StartRecording(); } private static void SampleChannel_PreVolumeMeter(object sender, StreamVolumeEventArgs e) { Debug.Print(""+ e.MaxSampleValues[0]); } private static void waveIn_DataAvailable(object sender, NAudio.Wave.WaveInEventArgs e) { AnalyzeVoice(e.Buffer); } static bool isbusy = false ; /// <summary> /// 语音分析 /// </summary> /// <param name="buf"></param> private async static void AnalyzeVoice(byte[] buffer) { if (isbusy) { return; } isbusy = true; await Task.Delay(10); float[] sts = new float[buffer.Length / 2]; int outIndex = 0; for (int n = 0; n < buffer.Length; n += 2) { sts[outIndex++] = BitConverter.ToInt16(buffer, n) / 32768f; } int channels = 20;//值越大提取的越稀疏 float value = 0; List<Point> listPoint = new List<Point>(); float p=0; float x = 0; for (int n = 0; n < sts.Length; n += channels) { value = sts[n] > 0.0f ? sts[n] *400: 1f;//sts[n] * 400 是为了转化成有用的高度值 Debug.Print(""+value );//这个就是大概的波形图 p = value; x += 2; listPoint.Add(new Point(x,p)); } UpdatePath(listPoint);//listPoint 更新一个类似贝塞尔曲线的效果 isbusy = false ; } private static void UpdatePath(List<Point> points) { StringBuilder data = new StringBuilder("M"); data.AppendFormat("{0},{1} C", points[0].X, points[0].Y); for (int i = 1; i < points.Count; i++) { Point pre = new Point((points[i - 1].X + points[i].X) / 2, points[i - 1].Y); //控制点 Point next = new Point((points[i - 1].X + points[i].X) / 2, points[i].Y); //控制点 data.AppendFormat(" {0},{1} {2},{3} {4},{5}", pre.X, pre.Y, next.X, next.Y, points[i].X, points[i].Y); } // pathData.path = new Path { Stroke = Brushes.DodgerBlue, StrokeThickness = 1, Data = Geometry.Parse(data.ToString()) }; pathData.path = Geometry.Parse(data.ToString()); } } public class SampleData : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; void NotifyPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } float _value = 0; public float value { get { return _value; } set { _value = value; NotifyPropertyChanged(nameof(value)); } } Geometry _path ; public Geometry path { get { return _path; } set { _path = value; NotifyPropertyChanged(nameof(path)); } } } }
fffffffffffffffff
test red font.