概述

在 NAudio 中, 常用类型有 WaveIn, WaveOut, WaveStream, WaveFileWriter, WaveFileReader 以及接口: IWaveProvider

  1. WaveIn 表示波形输入, 例如麦克风输入, 或者计算机正在播放的音频流.
  2. WaveOut 表示波形输出, 用来播放波形音乐, 以继承了 IWaveProvider 的类型作为播放源播放音乐
  3. WaveStream 表示波形流, 它继承了 IWaveProvider, 可以用来作为播放源.
  4. WaveFileReader 继承了 WaveStream, 用来读取波形文件
  5. WaveFileWriter 继承了 Stream, 用来写入文件, 常用于保存音频录制的数据
  6. IWaveProvider 上面已经提到, 是音频播放的提供者

播放音频

常用的播放音频方式有两种, 播放波形音乐, 以及播放 MP3 音乐

  1. 播放波形音乐:

    // NAudio 中, 通过 WaveFileReader 来读取波形数据, 在实例化时, 你可以指定文件名或者是输入流, 这意味着你可以读取内存流中的音频数据
    // 但是需要注意的是, 不可以读取来自网络流的音频, 因为网络流不可以进行 Seek 操作. // 此处, 假设 ms 为一个 MemoryStream, 内存有音频数据.
    WaveFileReader reader = new WaveFileReader(ms);
    WaveOut wout = new WaveOut();
    wout.Init(reader); // 通过 IWaveProvider 为音频输出初始化
    wout.Play(); // 至此, wout 将从指定的 reader 中提供的数据进行播放
  2. 播放 MP3 音乐:

    // 播放 MP3 音乐其实与播放波形音乐没有太大区别, 只不过将 WaveFileReader 换成了 Mp3FileReader 罢了
    // 另外, 也可以使用通用的 Reader, MediaFoundationReader, 它既可以读取波形音乐, 也可以读取 MP3 // 此处, 假设 ms 为一个 MemoryStream, 内存有音频数据.
    Mp3FileReader reader = new Mp3FileReader(ms);
    WaveOut wout = new WaveOut();
    wout.Init(reader);
    wout.Play();

音频录制

  1. 录制麦克风输入

    // 借助 WaveIn 类, 我们可以轻易的捕获麦克风输入, 在每一次录制到数据时, 将数据写入到文件或其他流, 这就实现了保存录音
    // 在保存波形文件时需要借助 WaveFileWriter, 当然, 如果你想保存为其他格式, 也可以使用其它的 Writer, 例如 CurWaveFileWriter 以及
    // AiffFileWriter, 美中不足的是没有直接写入到 MP3 的 FileWriter
    // 需要注意的是, 如果你是用的桌面程序, 那么你可以直接使用 WaveIn, 其回调基于 Windows 消息, 所以无法在控制台应用中使用 WaveIn
    // 如果要在控制台应用中实现录音, 只需要使用 WaveInEvent, 它的回调基于事件而不是 Windows 消息, 所以可以通用 WaveIn cap = new WaveIn(); // cap, capture
    WaveFileWriter writer = new WaveFileWriter();
    cap.DataAvailable += (s, args) => writer.Write(args.Buffer, 0, args.BytesRecorded); // 订阅事件
    cap.StartRecording(); // 开始录制 // 结束录制时:
    cap.StopRecording(); // 停止录制
    writer.Close(); // 关闭 FileWriter, 保存数据 // 另外, 除了使用 WaveIn, 你还可以使用 WasapiCapture, 它与 WaveIn 的使用方式是一致的, 可以用来录制麦克风
    // Wasapi 全称 Windows Audio Session Application Programming Interface (Windows音频会话应用编程接口)
    // 具体 WaveIn, WaveInEvent, WasapiCapture 的性能, 笔者还没有测试过, 但估计不会有太大差异.
    // 提示: WasapiCapture 和 WasapiLoopbackCapture 位于 NAudio.Wave 命名空间下
  2. 录制声卡输出

    // 录制声卡输出, 也就是录制计算机正在播放的声音, 借助 WasapiLoopbackCapture 即可简单实现, 使用方式与 WasapiCapture 无异
    
    WasapiLoopbackCapture cap = new WasapiLoopbackCapture();
    WaveFileWriter writer = new WaveFileWriter();
    cap.DataAvailable += (s, args) => writer.Write(args.Buffer, 0, args.BytesRecorded);
    cap.StartRecording();

高级应用

  1. 获取计算机实时播放音量大小

    // 其实这个是基于刚刚的录制声卡输出的, 录制时的回调中, Buffer, BytesRecorded 指定了此次录制的数据 (缓冲区和数据长度)
    // 而这些数据, 其实是计算机对声音的采样(Sample), 具体的采样格式可以查看 WasapiLoopbackCapture 实例的 WaveForamt
    // 波形格式中的 Encoding 与 BitsPerSample 是我们所需要的. 一般默认的 Encoding 是 IeeeFloat, 也就是每一个采样都是
    // 一个浮点数, 而 BitsPerSample 也就是 32 了. 通过 BitConverter.ToSingle() 我们可以从缓冲区中取得浮点数
    // 遍历, 每 32 位一个浮点数, 最终取最大值, 这就是我们所需要的音量了 float volume;
    WasapiLoopbackCapture cap = new WasapiLoopbackCapture();
    cap.DataAvailable += (s, args) => volume = Enumerable
    .Range(0, args.BytesRecorded / 4) // 每一个采样的位置
    .Select(i => BitConverter.ToSingle(args.Buffer, i * 4)) // 获取每一个采样
    .Aggregate((v1, v2) => v1 > v2 ? v1 : v2); // 找到值最大的采样
  2. 实现音乐可视化

    // 既然我们已经知道了, 那些数据都是一个个的采样, 自然也可以通过它们来绘制频谱, 只需要进行快速傅里叶变换即可
    // 而且有意思的是, NAudio 也为我们准备好了快速傅里叶变换的方法, 位于 NAudio.Dsp 命名空间下
    // 提示: 进行傅里叶变换所需要的复数(Complex)类也位于 NAudio.Dsp 命名空间, 它有两个字段, X(实部) 与 Y(虚部)
    // 下面给出在 IeeeFloat 格式下的音乐可视化的简单示例:
    WasapiLoopbackCapture cap = new WasapiLoopbackCapture();
    cap.DataAvailable += (s, args) =>
    {
    float[] samples = Enumerable
    .Range(0, args.BytesRecorded / 4)
    .Select(i => BitConverter.ToSingle(args.Buffer, i * 4))
    .ToArray(); // 获取采样 int log = (int)Math.Ceiling(Math.Log(samples.Length, 2));
    float[] filledSamples = new float[(int)Math.Pow(2, log)];
    Array.Copy(samples, filledSamples, samples.Length); // 填充数据 int sampleRate = (s as WasapiLoopbackCapture).WaveFormat.SampleRate; // 获取采样率
    Complex[] complexSrc = filledSamples.Select((v, i) =>
    {
    double deg = i / (double)sampleRate * Math.PI * 2; // 获取当前采样率在圆上对应的角度 (弧度制)
    return new Complex()
    {
    X = (float)(Math.Cos(deg) * v),
    Y = (float)(Math.Sin(deg) * v)
    };
    }).ToArray(); // 将采样转换为对应的复数 (缠绕到圆) FastFourierTransform.FFT(false, log, complexSrc); // 进行傅里叶变换
    double[] result = complexSrc.Select(v => Math.Sqrt(v.X * v.X + v.Y * v.Y)).ToArray(); // 取得结果
    };
  3. 音频格式转换

    // 对于 Wave, CueWave, Aiff, 这些格式都有其对应的 FileWriter, 我们可以直接调用其 Writer 的 Create***File 来
    // 从 IWaveProvider 创建对应格式的文件. 对于 MP3 这类没有 FileWriter 的格式, 可以调用 MediaFoundationEncoder // 例如一个文件, "./Disconnected.mp3", 我们要将它转换为 wav 格式, 只需要使用下面的代码, CurWave 与 Aiff 同理
    using (Mp3FileReader reader = new Mp3FileReader("./Disconnected.mp3"))
    WaveFileWriter.CreateWaveFile("./Disconnected.wav", reader); // 从 IWaveProvider 创建 MP3 文件, 假如一个 WaveFileReader 为 src
    MediaFoundationEncoder.EncodeToMp3(src, "./NewMp3.mp3");

提示

对于刚刚所说的音频录制, 采样的格式有一点需要注意, 将数据转换为一个 float 数组后, 其中还需要区分音频通道, 例如一般音乐是双通道, WaveFormat 的 Channel 为 2, 那么在 float 数组中, 每两个采样为一组, 一组采样中每一个采样都是一个通道在当前时间内的采样.

以双通道距离, 下图中, 采样数据中每一个圆圈都表示一个 float 值, 那么每两个采样时间点相同, 而各个通道的采样就是每一组中每一个采样

所以对于我们刚刚进行的音乐可视化, 严格意义上来讲, 还需要区分通道

示例

本文提到的部分内容在 github.com/SlimeNull/AudioTest 仓库中有示例, 例如音频可视化, 音频录制, 以及其他零星的示例


如有错误, 还请指出

[C#] NAudio 库的各种常用使用方式: 播放 录制 转码 音频可视化的更多相关文章

  1. [C#] 使用 NAudio 实现音频可视化

    预览: 捕捉声卡输出: 实现音频可视化, 第一步就是获得音频采样, 这里我们选择使用计算机正在播放的音频作为采样源进行处理: NAudio 中, 可以借助 WasapiLoopbackCapture ...

  2. 前后端常用通讯方式-- ajax 、websocket

    一.前后端常用通讯方式 1. ajax  浏览器发起请求,服务器返回数据,服务器不能主动返回数据,要实现实时数据交互只能是ajax轮询(让浏览器隔个几秒就发送一次请求,然后更新客户端显示.这种方式实际 ...

  3. jQuery中ajax的4种常用请求方式

    jQuery中ajax的4种常用请求方式: 1.$.ajax()返回其创建的 XMLHttpRequest 对象. $.ajax() 只有一个参数:参数 key/value 对象,包含各配置及回调函数 ...

  4. iOS代码加密常用加密方式

    iOS代码加密常用加密方式 iOS代码加密常用加密方式,常见的iOS代码加密常用加密方式算法包括MD5加密.AES加密.BASE64加密,三大算法iOS代码加密是如何进行加密的,且看下文 MD5 iO ...

  5. DataGridView 中添加CheckBox和常用处理方式 .

    DataGridView 中添加CheckBox和常用处理方式 文章1 转载:http://blog.csdn.net/pinkey1987/article/details/5267934 DataG ...

  6. Linux共享库两种加载方式简述

      Linux共享库两种加载方式简述  动态库技术通常能减少程序的大小,节省空间,提高效率,具有很高的灵活性,对于升级软件版本也更加容易.与静态库不同,动态库里面的函数不是执行程序本身 的一部分,而是 ...

  7. python常用执行方式&变量&input函数

    linux系统中执行py文件方式:  ./a.py 需要执行权限 chmod -R 777(最大权限) 常用执行方式: 1. ./a.py2. python a.py 文件内部头加上 #!/usr/b ...

  8. RAC集群数据库连库代码示例(jdbc thin方式,非oci)

    1.RAC集群数据库连库代码示例(jdbc thin方式,非oci):jdbc.driverClassName=oracle.jdbc.driver.OracleDriverjdbc.url=jdbc ...

  9. Linux 常用分区方式

    1 分两个区 主目录:/ 交换分区:swap 2 常用分区方式,以使用100G空间安装linux为例 引导分区: 挂载点/boot,分区格式ext4,500M以内即可 交换分区: 无挂载点,分区格式选 ...

随机推荐

  1. 第47天学习打卡(HTML)

    什么是HTML HTML Hyper Text Markup Language(超文本标记语言) 超文本包括:文字,图片,音频,视频,动画等 HTML5,提供了一些新的元素和一些有趣的新特性,同时也建 ...

  2. Django3.0 + nginx + uwsgi 部署

    CentOS7.6 下部署Django3.0应用,使用nginx+uwsgi部署: 1. uwsgi部署 pip install uwsgi 在项目的根目录中,新建文件夹 conf, 然后进入conf ...

  3. Linux graphics stack

    2D图形架构 早期Linux图形系统的显示全部依赖X Server,X Client调用Xlib提供的借口向 X Server发送渲染命令,X Server根据 X Client的命令请求向硬件设备绘 ...

  4. vscode中js文件使用typescript语法报错,如何解决

    原因:由于vcode自身的语法检查有些问题 解决办法:在设置里面加上 "javascript.validate.enable": false 禁用默认的 js 验证 总结: 由于v ...

  5. 基于角色访问控制RBAC权限模型的动态资源访问权限管理实现

    RBAC权限模型(Role-Based Access Control) 前面主要介绍了元数据管理和业务数据的处理,通常一个系统都会有多个用户,不同用户具有不同的权限,本文主要介绍基于RBAC动态权限管 ...

  6. 漏洞复现-CVE-2016-4437-Shiro反序列化

        0x00 实验环境 攻击机:Win 10 靶机也可作为攻击机:Ubuntu18 (docker搭建的vulhub靶场)(兼顾反弹shell的攻击机) 0x01 影响版本 Shiro <= ...

  7. 微信小程序自定义Tabber,附详细源码

    目录 1,前言 2,说明 3,核心代码 1,前言 分享一个完整的微信小程序自定义Tabber,tabber按钮可以设置为跳转页面,也可以设置为功能按钮.懒得看文字的可以直接去底部,博主分享了小程序代码 ...

  8. WAV16T VPX国产化千兆交换板

      WAV16T是基于盛科CTC5160设计的国产化3U三层千兆VPX交换板,提供16路千兆电口,采用龙芯 2K1000处理器.支持常规的L2/L3协议,支持Telnet.SNMP.WEB,CLI等多 ...

  9. Azure AD, Endpoint Manger(Intune), SharePoint access token 的获取

    本章全是干货,干货,干货,重要的事情说三遍. 最近在研究Azure, Cloud相关的东西,项目中用的是Graph API(这个在下一章会相信介绍),可能是Graph API推出的时间比较晚,部分AP ...

  10. Mysql在windows环境如何修改密码?

    1.关闭正在运行的MySQL服务. 2. 打开DOS窗口,转到mysql\bin目录. 3. 输入mysqld --skip-grant-tables 回车.--skip-grant-tables 的 ...