下午写了一篇关于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 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的格式设置,上述代码都是基于通道数为2、PCM为16bit的情况下,如这两项修改会发生转换和调用失败等问题

    可调试Demo:示例Demo

posted on 2019-08-30 16:11  Khandasas  阅读(10960)  评论(1编辑  收藏  举报