下午写了一篇关于NAudio的录音、播放和波形图的博客,不太满意,感觉写的太乱,又总结了下
NAudio是个相对成熟、开源的C#音频开发工具,它包含录音、播放录音、格式转换、混音调整等功能。本次介绍主要功能有音频、录音文件播放、实时音频流波形图显示等。具体如下:
1. 录音
NAudio录音主要使用WaveIn和WaveFileWriter两个类
1.1 WaveIn
WaveIn的功能是对录音的音频参数进行设置以及对数据的采集,参数如通道数、采样率、平均数据传输速率(WaveFormat)、数据回调事件、录音停止回调函数等参数
其中,DataAvailable为数据回调参数,是在录音时实时将录音数据传递出来,有需要使用录音数据的可以订阅该事件进行接收业务和相关处理。
1.2 WaveFileWriter
该类是创建相对应格式的音频文件,并提供想对应的写入数据方法、保存方法等,具体如下:
public class WaveFileWriter : Stream { public WaveFileWriter(Stream outStream, WaveFormat format); public WaveFileWriter(string filename, WaveFormat format); ~WaveFileWriter(); public override long Position { get; set; } public override bool CanWrite { get; } public override bool CanRead { get; } public WaveFormat WaveFormat { get; } public TimeSpan TotalTime { get; } public override long Length { get; } public string Filename { get; } public override bool CanSeek { get; } public static void CreateWaveFile(string filename, IWaveProvider sourceProvider); public static void CreateWaveFile16(string filename, ISampleProvider sourceProvider); public static void WriteWavFileToStream(Stream outStream, IWaveProvider sourceProvider); public override void Flush(); public override int Read(byte[] buffer, int offset, int count); public override long Seek(long offset, SeekOrigin origin); public override void SetLength(long value); public override void Write(byte[] data, int offset, int count); [Obsolete("Use Write instead")] public void WriteData(byte[] data, int offset, int count); [Obsolete("Use WriteSamples instead")] public void WriteData(short[] samples, int offset, int count); public void WriteSample(float sample); public void WriteSamples(short[] samples, int offset, int count); public void WriteSamples(float[] samples, int offset, int count); protected override void Dispose(bool disposing); protected virtual void UpdateHeader(BinaryWriter writer); }
在调用上是先调用WaveIn的DataAvailable回调函数,读取其数据并写入流文件,最后保存到本地。
2. 播放录音
播放录音主要用到AudioFileReader、WaveOut三个类和接口
2.1 AudioFileReader
AudioFileReader主要负责读取音频文件,验证音频文件格式,对外部提供读取数据接口,具体如下:
public class AudioFileReader : WaveStream, ISampleProvider { public AudioFileReader(string fileName); public string FileName { get; } public override WaveFormat WaveFormat { get; } public override long Length { get; } public override long Position { get; set; } public float Volume { get; set; } public override int Read(byte[] buffer, int offset, int count); public int Read(float[] buffer, int offset, int count); protected override void Dispose(bool disposing); }
2.2 WaveOut
WaveOut的工作是播放音频,它调用AudioFileReader.Read进行数据读取,对读取的数据进行播放,主要工作流程是从获取数据,并将数据进行播放成音频
public class WaveOut : IWavePlayer, IDisposable, IWavePosition { public WaveOut(); public WaveOut(IntPtr windowHandle); public WaveOut(WaveCallbackInfo callbackInfo); ~WaveOut(); public static int DeviceCount { get; } public PlaybackState PlaybackState { get; } public WaveFormat OutputWaveFormat { get; } public int DeviceNumber { get; set; } public int NumberOfBuffers { get; set; } public int DesiredLatency { get; set; } public float Volume { get; set; } public event EventHandlerPlaybackStopped; public static WaveOutCapabilities GetCapabilities(int devNumber); public void Dispose(); public long GetPosition(); public void Init(IWaveProvider waveProvider); public void Pause(); public void Play(); public void Resume(); public void Stop(); protected void Dispose(bool disposing); }
3. 波形图绘制
录音时绘制波形图需要在DataAviliable回调函数中获取音频数据并将其从byte[]转换为float[],然后用float[]数据做为波形图的输入即可,这个过程源码上写一个数据包的波形图数据为waveSource.WaveFormat.SampleRate / 100,原理上我还没搞懂,但是的确是这么操作显示是对的,具体如下:
private void waveSource_DataAvailable(object sender, WaveInEventArgs e) { if (waveFile != null) { waveFile.Write(e.Buffer, 0, e.BytesRecorded); waveFile.Flush(); float[] sts = new float[e.Buffer.Length / 2]; int outIndex = 0; for (int n = 0; n < e.Buffer.Length; n += 2) { sts[outIndex++] = BitConverter.ToInt16(e.Buffer, n) / 32768f; } for (int n = 0; n < sts.Length; n += channels) { Add(sts[n]); } } }
需要注意的是WaveFormat的通道数设置、PCM的格式设置,上述代码都是基于通道数为2、PCM为16bit的情况下,如这两项修改会发生转换和调用失败等问题
可调试Demo:示例Demo