下午寫了一篇關於NAudio的錄音、播放和波形圖的博客,不太滿意,感受寫的太亂,又總結了下ide
NAudio是個相對成熟、開源的C#音頻開發工具,它包含錄音、播放錄音、格式轉換、混音調整等功能。本次介紹主要功能有音頻、錄音文件播放、實時音頻流波形圖顯示等。具體以下:函數
1. 錄音工具
NAudio錄音主要使用WaveIn和WaveFileWriter兩個類開發工具
1.1 WaveInspa
WaveIn的功能是對錄音的音頻參數進行設置以及對數據的採集,參數如通道數、採樣率、平均數據傳輸速率(WaveFormat)、數據回調事件、錄音中止回調函數等參數 .net
其中,DataAvailable爲數據回調參數,是在錄音時實時將錄音數據傳遞出來,有須要使用錄音數據的能夠訂閱該事件進行接收業務和相關處理。調試
1.2 WaveFileWritercode
該類是建立相對應格式的音頻文件,並提供想對應的寫入數據方法、保存方法等,具體以下:orm
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回調函數,讀取其數據並寫入流文件,最後保存到本地。blog
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 EventHandler<StoppedEventArgs> PlaybackStopped; 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的格式設置,上述代碼都是基於通道數爲二、PCM爲16bit的狀況下,如這兩項修改會發生轉換和調用失敗等問題
可調試Demo:示例Demo