?
? ? ?下午寫了一篇關(guān)于NAudio的錄音、播放和波形圖的博客,不太滿意,感覺寫的太亂,又總結(jié)了下
? ? ?
NAudio是個(gè)相對成熟、開源的C#音頻開發(fā)工具,它包含錄音、播放錄音、格式轉(zhuǎn)換、混音調(diào)整等功能。本次介紹主要功能有音頻、錄音文件播放、實(shí)時(shí)音頻流波形圖顯示等。具體如下:
1. 錄音
? ? ? NAudio錄音主要使用WaveIn和WaveFileWriter兩個(gè)類
? ?1.1?? WaveIn
? ? ?
WaveIn的功能是對錄音的音頻參數(shù)進(jìn)行設(shè)置以及對數(shù)據(jù)的采集,參數(shù)如通道數(shù)、采樣率、平均數(shù)據(jù)傳輸速率(WaveFormat)、數(shù)據(jù)回調(diào)事件、錄音停止回調(diào)函數(shù)等參數(shù)?
? ? ? 其中,DataAvailable為數(shù)據(jù)回調(diào)參數(shù),是在錄音時(shí)實(shí)時(shí)將錄音數(shù)據(jù)傳遞出來,有需要使用錄音數(shù)據(jù)的可以訂閱該事件進(jìn)行接收業(yè)務(wù)和相關(guān)處理。
? ?1.2?? WaveFileWriter
? ? ?該類是創(chuàng)建相對應(yīng)格式的音頻文件,并提供想對應(yīng)的寫入數(shù)據(jù)方法、保存方法等,具體如下:
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); }
? ? ?在調(diào)用上是先調(diào)用WaveIn的DataAvailable回調(diào)函數(shù),讀取其數(shù)據(jù)并寫入流文件,最后保存到本地。
2. 播放錄音
播放錄音主要用到AudioFileReader、WaveOut三個(gè)類和接口
? ? ? 2.1?? AudioFileReader
? ? ? ? ? AudioFileReader主要負(fù)責(zé)讀取音頻文件,驗(yàn)證音頻文件格式,對外部提供讀取數(shù)據(jù)接口,具體如下:
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的工作是播放音頻,它調(diào)用AudioFileReader.Read進(jìn)行數(shù)據(jù)讀取,對讀取的數(shù)據(jù)進(jìn)行播放,主要工作流程是從獲取數(shù)據(jù),并將數(shù)據(jù)進(jìn)行播放成音頻
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. 波形圖繪制
? ? ?
?錄音時(shí)繪制波形圖需要在DataAviliable回調(diào)函數(shù)中獲取音頻數(shù)據(jù)并將其從byte[]轉(zhuǎn)換為float[],然后用float[]數(shù)據(jù)做為波形圖的輸入即可,這個(gè)過程源碼上寫一個(gè)數(shù)據(jù)包的波形圖數(shù)據(jù)為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的通道數(shù)設(shè)置、PCM的格式設(shè)置,上述代碼都是基于通道數(shù)為2、PCM為16bit的情況下,如這兩項(xiàng)修改會發(fā)生轉(zhuǎn)換和調(diào)用失敗等問題
? ? 可調(diào)試Demo:示例Demo <https://download.csdn.net/download/qq_17005083/11644031>
熱門工具 換一換