zoukankan      html  css  js  c++  java
  • C# NAudio录音和播放音频文件及实时绘制音频波形图(从音频流数据获取,而非设备获取)

     

         下午写了一篇关于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

  • 相关阅读:
    C#多线程编程实战1.5检测线程状态
    C#多线程编程实战1.4终止线程
    C#多线程编程实战1.3等待线程
    C#多线程编程实战1.2暂停线程(休眠)
    C#多线程编程实战1.1创建线程
    C#中base的作用
    C#继承
    C#中return的两个作用
    Windows下完全卸载node.js并安装node.js的多版本管理工具nvm-windows
    执行gulp build报错
  • 原文地址:https://www.cnblogs.com/Khan-Sadas/p/11435536.html
Copyright © 2011-2022 走看看