C# NAudio 库的各种常见使用方式之播放 录制 转码 音频可视化

概述

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

  • WaveIn 表示波形输入, 继承了 IWaveIn, 例如麦克风输入, 或者计算机正在播放的音频流.
  • WaveOut 表示波形输出, 继承了 IWavePlayer, 用来播放波形音乐, 以 IWaveProvider 作为播放源播放音频, 通过拓展方法也支持以 ISampleProvider 作为播放源播放音频
  • WaveStream 表示波形流, 它继承了 IWaveProvider, 可以用来作为播放源.
  • WaveFileReader 继承了 WaveStream, 用来读取波形文件
  • WaveFileWriter 继承了Stream, 用来写入文件, 常用于保存音频录制的数据.
  • AudioFileReader 通用的音频文件读取器, 可以读取波形文件, 也可以读取其他类型的音频文件例如 Aiff, MP3
  • IWaveProvider 波形提供者, 上面已经提到, 是音频播放的提供者, 通过拓展方法可以转换为 ISampleProvider
  • ISampleProvider 采样提供者, 上面已经提到, 通过拓展方法可以作为 WaveOut 的播放源

播放音频

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

播放波形音乐:

// NAudio 中, 通过 WaveFileReader 来读取波形数据, 在实例化时, 你可以指定文件名或者是输入流, 这意味着你可以读取内存流中的音频数据
// 但是需要注意的是, 不可以读取来自网络流的音频, 因为网络流不可以进行 Seek 操作.

// 此处, 假设 ms 为一个 MemoryStream, 内存有音频数据.
WaveFileReader reader = new WaveFileReader(ms);
WaveOut wout = new WaveOut();
wout.Init(reader);             // 通过 IWaveProvider 为音频输出初始化
wout.Play();                   // 至此, wout 将从指定的 reader 中提供的数据进行播放

播放 MP3 音乐:

// 播放 MP3 音乐其实与播放波形音乐没有太大区别, 只不过将 WaveFileReader 换成了 Mp3FileReader 罢了
// 另外, 也可以使用通用的 Reader, MediaFoundationReader, 它既可以读取波形音乐, 也可以读取 MP3

// 此处, 假设 ms 为一个 MemoryStream, 内存有音频数据.
Mp3FileReader reader = new Mp3FileReader(ms);
WaveOut wout = new WaveOut();
wout.Init(reader);
wout.Play();

音频录制

录制麦克风输入

// 借助 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 命名空间下

录制声卡输出

// 录制声卡输出, 也就是录制计算机正在播放的声音, 借助 WasapiLoopbackCapture 即可简单实现, 使用方式与 WasapiCapture 无异

WasapiLoopbackCapture cap = new WasapiLoopbackCapture();
WaveFileWriter writer = new WaveFileWriter();
cap.DataAvailable += (s, args) => writer.Write(args.Buffer, 0, args.BytesRecorded);
cap.StartRecording();

高级应用

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

// 既然我们已经知道了, 那些数据都是一个个的采样, 自然也可以通过它们来绘制频谱, 只需要进行快速傅里叶变换即可
// 而且有意思的是, 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 => new Complex(){ X = v }).ToArray();  // 转换为复数
    FastFourierTransform.FFT(false, log, complexSrc);     // 进行傅里叶变换
    double[] result = complexSrc.Select(v => Math.Sqrt(v.X * v.X + v.Y * v.Y)).ToArray();    // 取得结果
};

音频格式转换

// 对于 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 值, 那么每两个采样时间点相同, 而各个通道的采样就是每一组中每一个采样

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

对于采样的大小, BitsPerSample, 可以是 8, 16, 32, 其中 8 和 16 都是整数存储采样, 32 是浮点数存储采样.

另外, 对于音乐可视化部分的傅里叶变换结果, 我们只需要其中一部分, 取前 256 个足矣. (我也不知道它这个运算结果是如何分布的)

示例

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

到此这篇关于C# NAudio 库的各种常见使用方式之播放 录制 转码 音频可视化的文章就介绍到这了,更多相关C# NAudio 库用法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C# 实现特殊字符快速转码

    我就废话不多说了,大家还是直接看代码吧~ encodeURIComponent('\n') "%0A" encodeURIComponent('\\') "%5C" encodeURIComponent('/') "%2F" encodeURIComponent(',') "%2C" encodeURIComponent('\'') %27 encodeURIComponent("\"") &qu

  • C#使用 NAudio 实现音频可视化的方法

    预览: 捕捉声卡输出: 实现音频可视化, 第一步就是获得音频采样, 这里我们选择使用计算机正在播放的音频作为采样源进行处理: NAudio 中, 可以借助 WasapiLoopbackCapture 来进行捕捉: WasapiLoopbackCapture cap = new WasapiLoopbackCapture(); cap.DataAvailable += (sender, e) => // 录制数据可用时触发此事件, 参数中包含音频数据 { float[] allSamples =

  • C# NAudio 库的各种常见使用方式之播放 录制 转码 音频可视化

    概述 在 NAudio 中, 常用类型有 WaveIn, WaveOut, WaveStream, WaveFileWriter, WaveFileReader, AudioFileReader 以及接口: IWaveProvider, ISampleProvider, IWaveIn, IWavePlayer WaveIn 表示波形输入, 继承了 IWaveIn, 例如麦克风输入, 或者计算机正在播放的音频流. WaveOut 表示波形输出, 继承了 IWavePlayer, 用来播放波形音乐

  • 优化 Docker 镜像大小常见的方式

    平时我们构建的 Docker 镜像通常比较大,占用大量的磁盘空间,随着容器的大规模部署,同样也会浪费宝贵的带宽资源.本文将介绍几种常用的方法来优化 Docker 镜像大小,这里我们使用 Docker Hub 官方上的 Redis 镜像进行说明. 手动管理 我们能够直接想到的方法就是直接修改官方的 Redis 镜像 Dockerfile 文件,手动删除容器运行后不需要的组件,然后重新构建一个新镜像.这种方法理论上是可行的,但是容易出错,而且效果也不是特别明显.主要是不能和官方的镜像实时同步. 多阶

  • python代码的几种常见加密方式分享

    目录 1.发行.pyc文件 1.1 编译加密 1.2 是否可逆 1.3是否影响模块调用 2.代码混淆 3.生成exe可执行文件 4…py文件转化为.so文件 5.附加运行辅助包文件 方式一:高级加密模式,需要引导代码 方式二:超级加密模式 方式三: 虚拟加密模式 Python的文件类型介绍: .py python的源代码文件 .pyc Python源代码import后,编译生成的字节码 .pyo Python源代码编译优化生成的字节码.pyo比pyc并没有优化多少,只是去掉了断言 .pyd Py

  • PHP守护进程的两种常见实现方式详解

    本文实例讲述了PHP守护进程的两种常见实现方式.分享给大家供大家参考,具体如下: 第一种方式,借助 nohup 和 &  配合使用. 在命令后面加上 & 符号, 可以让启动的进程转到后台运行,而不占用控制台,控制台还可以再运行其他命令,这里我使用一个while死循环来做演示,代码如下 <?php while(true){ echo time().PHP_EOL; sleep(3); } 用 & 方式来启动该进程 [root@localhost php]# php deadlo

  • Javascript页面跳转常见实现方式汇总

    本文实例总结了Javascript页面跳转常见实现方式.分享给大家供大家参考,具体如下: 概述 相信很多Web开发者都知道,在开发Web程序的时候,对于页面之间的跳转,有很多种,但是有效的跳转则事半功倍,下面就是我在平时的开发过程中所用到的一些JavaScript跳转方式,拿出和大家共享一下. 第一种:直接跳转加参数 <script language="javascript" type="text/javascript"> window.location

  • Python2/3中urllib库的一些常见用法

    什么是Urllib库 Urllib是Python提供的一个用于操作URL的模块,我们爬取网页的时候,经常需要用到这个库. 升级合并后,模块中的包的位置变化的地方较多. urllib库对照速查表 Python2.X Python3.X urllib urllib.request, urllib.error, urllib.parse urllib2 urllib.request, urllib.error urllib2.urlopen urllib.request.urlopen urllib.

  • tangram.js库实现js类的方式实例分析

    本文实例讲述了tangram.js库实现js类的方式.分享给大家供大家参考,具体如下: 前面一篇http://www.jb51.net/article/25781.htm对tangram.js库做了较为详细的介绍,这里结合实例分析一下tangram.js库实现js类的方式.代码如下: /* * Tangram * Copyright 2010 Baidu Inc. All rights reserved. * * @author: meizz * @namespace: baidu.lang.c

  • Python第三方库的几种安装方式(小结)

    对于python开发用户而言,经常需要安装一些python的第三方库,但是第三方库的安装经常出错,以下给大家介绍一下python安装第三方库的几种常用方式: pip安装 无论是Windows.Linux还是Mac,都可以通过pip这个包管理工具来安装第三方库.最简单的安装方式就是: pip install requests pip默认是通过国外的源进行下载,速度太慢,且经常容易报错:因此推荐大家几个国内常用的安装源: 新版ubuntu要求使用https源,要注意. 清华:https://pypi

  • 分析MySQL抛出异常的几种常见解决方式

    前言 报错如下: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: Connections could not be acquired from the underlying database!   对于此类错误,直接看释义,一句话:JDBC 驱动抛出异常,连不上数据库. 一.代码配置的数据库名称或者密码与本地数据库不一致 1.1.错误产生描述 第一种,也是最为常见

随机推荐