Naudio是集录音、播放的源码库。https://github.com/naudio/NAudio,这是基于.net的框架。 在这链接的源码里有各种.net 框架的例子,对于基于.net wpf/universal 的程序,还提供了波形图的绘制。

我从没接触过音频类的技术,有个基于wpf 的项目需要快速提供实时绘制录音的图形。

因为源码库提供的wpf例子是基于文件播放的,其还中包括了比较多参数设置,技术上用了很多反射。很不直观 ,又因为对音频api毫无概念,想快速修改为基于实时录音还是需要费点时间的。不过可能需要修改波形图的样式,最后还是稍等研究一下例子,把图形绘制出来了。

先上一下效果,希望可以让你有兴趣读完和推荐。

在这里篇博文中,我将简述一下绘制录音及实时输出波形和回放及输出波形,并且尽量附近上所有代码,以方便需要节省时间的使用。

录音主要是需要用到WaveInEvent,WaveInEvent类提供了对音频参数进行设置以及对数据的采集,参数如通道数、采样率、平均数据传输速率(WaveFormat)。还提供了数据回调事件、录音停止回调函数等参数。 其中,DataAvailable为数据回调参数,是在录音时实时将录音数据传递出来,有需要使用录音数据的可以订阅该事件进行接收业务和相关处理。 在实时绘制波形图时, 我们就需要订阅DataAvailable事件以获得波形的数据源。

  1. 保存录音文件

为了保存录音数据,需要用到WaveFileWriter, 该类是创建相对应格式的音频文件,并提供想对应的写入数据方法、保存方法等。

  1. 绘制波形图

以下一段是对源码demo的解读,可以绕过,简单来说,可以认为波形图形控件(PolylineWaveFormControl)是已有,我们需要为图形提供数据源,就叫为(waveProvider)波形提供器吧。

参考NAudio源码中的NAudioWpfDemo,绘制波形图的类主要是PolylineWaveFormControl和PolygonWaveFormControl,以PolylineWaveFormControl为例,先不要考虑曲线是怎么画出来(其实也很简单,音频的采样值,采样值到来的顺序构成每个绘制的点数据,比如第一个采样值为500,则点为(0,500),依次类推)。

这个图形控件是可以直接拿来使用的,如果不需要修改波形图的样式。就算为了修改样式,也照着这两个例子的模式写一下就可以了。如果明白了如何在录音或者播放音频时得到采样值并将采样值传给这个类,那么修改起来也是很简单。

PolylineWaveFormControl和PolygonWaveFormControl,以PolylineWaveFormControl都实现了IWaveFormRenderer中的void AddValue(float maxValue, float minValue),这个AddValue函数在PolylineWaveFormVisualization的OnMaxCalculated(float min, float max)被调用,然后实际上又被AudioPlayback的MaximumCalculated调用,AudioPlayback的MaximumCalculated最终是被SampleAggregator中的MaximumCalculated调用。而在SampleAggregator中MaximumCalculated是被read调用的。read是在SampleAggregator对象关联的音频文件play之后被调用的。

如文章一开始所提,这个例子因为各种原因写得太绕了,不直观。从音频数据到图形,其实就是音频数据流到 SampleAggregator(可视为图形的Provider),SampleAggregator到PolylineWaveFormControl,如下:

因对于实时录音,只需要在WaveInEvent的DataAvailable中调用Read(…..)。

按照以上步骤的代码实现如下:

1.初始化录音对象,存储对象及图形提供器

其中WaveBufferProvider和RecorderBufferProvider是我实现的图形提供器。因为不管是实时录音还是回放文件,提供器的实现方式关键代码都是相同。只是read的实现方式稍微有点区别。将两者共同的抽象为基类WaveBufferProvider。这两个类的代码在文章后面全部提供。

2.将录音数据源传给提供器

70行调用provider的read方法,如上图所绘制,read将调用MaximumCalculated,在我这个实现的AudioRecorder中,24行为MaximumCalculated赋值如下:

然后在调用AudioRecorder和PolylineWaveFormControl的UI对象里为RenderAction赋值如下:

回放的例子可以结合我的代码解读和直接使用源码NAudioWpfDemo的“AudioPlayback“图形Proivder基本可以这么实现。

整个可运行工程的代码在此https://download.csdn.net/download/mochounv/12958596。

为方便无法下载源码的读者快速使用,我将整个provider类源码也提供如下:

public abstract class WaveBufferProvider: ISampleProvider
        // volume
        public event EventHandler<MaxSampleEventArgs> MaximumCalculated;
        private float maxValue;
        private float minValue;
        public int NotificationCount { get; set; }
        int count;
        // FFT
        public event EventHandler<FftEventArgs> FftCalculated;
        public bool PerformFFT { get; set; }
        private readonly Complex[] fftBuffer;
        private readonly FftEventArgs fftArgs;
        private int fftPos;
        private readonly int fftLength;
        private readonly int m;
        // private readonly ISampleProvider source;
        protected readonly int channels=1;
        public WaveBufferProvider(WaveFormat waveFormat,int fftLength = 1024)
            channels = waveFormat.Channels;
            m = (int)Math.Log(fftLength, 2.0);
            this.fftLength = fftLength;
            fftBuffer = new Complex[fftLength];
            fftArgs = new FftEventArgs(fftBuffer);
            WaveFormat = waveFormat;
        public void Reset()
            count = 0;
            maxValue = minValue = 0;
        protected void Add(float value)
            if (PerformFFT && FftCalculated != null)
                fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftLength));
                fftBuffer[fftPos].Y = 0;
                fftPos++;
                if (fftPos >= fftBuffer.Length)
                    fftPos = 0;
                    // 1024 = 2^10
                    FastFourierTransform.FFT(true, m, fftBuffer);
                    FftCalculated(this, fftArgs);
            maxValue = Math.Max(maxValue, value);
            minValue = Math.Min(minValue, value);
            count++;
            if (count >= NotificationCount && NotificationCount > 0)
                MaximumCalculated?.Invoke(this, new MaxSampleEventArgs(minValue, maxValue));
                Reset();
        WaveFormat mWaveFormat;
        public WaveFormat WaveFormat
                return mWaveFormat;
                mWaveFormat = value;
                NotificationCount = value.SampleRate / 100;
        virtual public int Read(float[] buffer, int offset, int count)
            return count;
 public class RecorderBufferProvider: WaveBufferProvider
        public RecorderBufferProvider(WaveFormat waveFormat, int fftLength = 1024):base(waveFormat,fftLength)
        override public int Read(float[] buffer, int offset, int count)
            for (int n = 0; n < count; n += channels)
                Add(buffer[n + offset]);
            return count;
 public class PlayerBufferProvider: WaveBufferProvider
        private readonly ISampleProvider source;
        public PlayerBufferProvider(ISampleProvider source, int fftLength = 1024) : base(source.WaveFormat, fftLength)
            this.source = source;
        override public int Read(float[] buffer, int offset, int count)
            var samplesRead = source.Read(buffer, offset, count);
            for (int n = 0; n < samplesRead; n += channels)
                Add(buffer[n + offset]);
            return samplesRead;
    public class MaxSampleEventArgs : EventArgs
        [DebuggerStepThrough]
        public MaxSampleEventArgs(float minValue, float maxValue)
            MaxSample = maxValue;
            MinSample = minValue;
        public float MaxSample { get; private set; }
        public float MinSample { get; private set; }
    public class FftEventArgs : EventArgs
        [DebuggerStepThrough]
        public FftEventArgs(Complex[] result)
            Result = result;
        public Complex[] Result { get; private set; }

因为wpf/uwp有基于driectx的绘图API,可以增量的绘制音频图形,效率比较高。对于winform等其它基于gui+的win32程序,就无法这样增量绘图,而是将图形绘制我图片上,通过更新图片来更换数据。

例子在此https://github.com/naudio/NAudio.WaveFormRenderer

https://blog.csdn.net/li_shidong/article/details/104797827

此链接揭示了provider中NotificationCount关键参数。

https://blog.csdn.net/weixin_30768661/article/details/102092545?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight

using VoiceRecorder.Core; using System.Windows.Input; using System.Collections.ObjectModel; using VoiceRecorder.Audio; using System.IO; using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Messaging; using System.Windows; using System.Threading; using GalaSoft.MvvmLight.Threading; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Threading.Tasks; usi... 上位机开发过程,信号数据是最常遇到的,在采集到信号数据后,如果能更好的展示成了难题。刚好最近接手了一个脑电信号数据的采集的项目,需要实时采集脑电信号并以波形展示出来。经过一番调研,网上有不少开源的形控件用于波形的展示,比如oxyplot,scottplot,livecharts等,在尝试后发现oxyplot采用MVVM设计,非常符合WPF的开发,Scottplot比较符合Winform的开发,而且接口也比较奇怪,livecharts绘特别漂亮,但有性能问题,比如绘制大数据点时会特别卡。... <Grid.RowDefinitions> <RowDefinition Height="40"></RowDefinition> <RowDefinition Height="*"></R... 百度查了一圈,NAudio相关文资料较少。鉴于本人最近在使用此库的播放音频方面有所涉及,在此将自己的学习过程与经验总结与大家分享,丰富.NET圈相关知识,同时也欢迎大佬探讨和指正。 为什么使用NAudio NAudio为.NET平台下的开源库,采用ML-PL协议,开源地址:https://github.com/naudio/NAudio。截至今日,已有约2.3k的stars。 NAu... <UserControl.Resources> <Storyboard x:Key="WaterStoryboard"> <PointAnimation Storyboard.TargetName="bs_Water" Storyboard.TargetProperty="Point1" From="15,-5" To="15,5" Duration="00:00:2" AutoReverse="True" RepeatBeha... 一、前言大家好,我是19944号万技师,来自湖南,以手法精湛而著称,目前在TGideas兼职重构工作。最近和我们部长aiden在做一个Motion的移动端组件库,被很多客户吐槽没有使用很频繁的音频组件。之前觉得,音频这一块功能较为简单,不需要沉淀组件。但经过一番琢磨,却做出了不少有意思的东西。比如:分析音频生成动画;部分设备同时播放(web audio支持同时播放);音频特色,比如lol英雄低沉的... 下午写了一篇关于NAudio录音、播放和波形的博客,不太满意,感觉写的太乱,又总结了下NAudio是个相对成熟、开源的C#音频开发工具,它包含录音、播放录音、格式转换、混音调整等功能。本次介绍主要功能有音频、录音文件播放、实时音频流波形显示等。具体如下:1. 录音NAudio录音主要使用WaveIn和WaveFileWriter两个类1.1 WaveInWaveIn的功能是对录音的音频参... 最近在做微软的一点小项目需要用到语音识别,但是微软的语音识别真的太不给力了,其遇到很多麻烦。偶然听到大熊说google的语音识别接口,于是搜索资料弄了一个,整理出来,希望能帮助需要的朋友,大神别喷就好。 一、使用Google Speech  API 思路解析:   1、首先通过WPF录音,这里注意码率必 调用API 调用百度语音识别API实现语音识别(ASR)与语音合成(TTS),分别需要发送一个HTTP请求。 Note: 在发送ASR或者TTS请求前,首先需要获取Access Token; 1. 获取Token 通过百度开发者账号获取到key与secret key,然后通过以下请求得到token;//Access Token(每次获取后,有效期:一个月)string getAccessUr <audio controls> <source src="audiofile.mp3" type="audio/mpeg"> <source src="audiofile.ogg" type="audio/ogg">