[C#] NAudio 库的各种常用使用方式: 播放 录制 转码 音频可视化
概述
在 NAudio 中, 常用类型有 WaveIn, WaveOut, WaveStream, WaveFileWriter, WaveFileReader 以及接口: IWaveProvider
- WaveIn 表示波形输入, 例如麦克风输入, 或者计算机正在播放的音频流.
- WaveOut 表示波形输出, 用来播放波形音乐, 以继承了 IWaveProvider 的类型作为播放源播放音乐
- WaveStream 表示波形流, 它继承了 IWaveProvider, 可以用来作为播放源.
- WaveFileReader 继承了 WaveStream, 用来读取波形文件
- WaveFileWriter 继承了 Stream, 用来写入文件, 常用于保存音频录制的数据
- IWaveProvider 上面已经提到, 是音频播放的提供者
播放音频
常用的播放音频方式有两种, 播放波形音乐, 以及播放 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();
高级应用
获取计算机实时播放音量大小
// 其实这个是基于刚刚的录制声卡输出的, 录制时的回调中, 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); // 找到值最大的采样
实现音乐可视化
// 既然我们已经知道了, 那些数据都是一个个的采样, 自然也可以通过它们来绘制频谱, 只需要进行快速傅里叶变换即可
// 而且有意思的是, 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(); // 取得结果
};
音频格式转换
// 对于 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 库的各种常用使用方式: 播放 录制 转码 音频可视化的更多相关文章
- [C#] 使用 NAudio 实现音频可视化
预览: 捕捉声卡输出: 实现音频可视化, 第一步就是获得音频采样, 这里我们选择使用计算机正在播放的音频作为采样源进行处理: NAudio 中, 可以借助 WasapiLoopbackCapture ...
- 前后端常用通讯方式-- ajax 、websocket
一.前后端常用通讯方式 1. ajax 浏览器发起请求,服务器返回数据,服务器不能主动返回数据,要实现实时数据交互只能是ajax轮询(让浏览器隔个几秒就发送一次请求,然后更新客户端显示.这种方式实际 ...
- jQuery中ajax的4种常用请求方式
jQuery中ajax的4种常用请求方式: 1.$.ajax()返回其创建的 XMLHttpRequest 对象. $.ajax() 只有一个参数:参数 key/value 对象,包含各配置及回调函数 ...
- iOS代码加密常用加密方式
iOS代码加密常用加密方式 iOS代码加密常用加密方式,常见的iOS代码加密常用加密方式算法包括MD5加密.AES加密.BASE64加密,三大算法iOS代码加密是如何进行加密的,且看下文 MD5 iO ...
- DataGridView 中添加CheckBox和常用处理方式 .
DataGridView 中添加CheckBox和常用处理方式 文章1 转载:http://blog.csdn.net/pinkey1987/article/details/5267934 DataG ...
- Linux共享库两种加载方式简述
Linux共享库两种加载方式简述 动态库技术通常能减少程序的大小,节省空间,提高效率,具有很高的灵活性,对于升级软件版本也更加容易.与静态库不同,动态库里面的函数不是执行程序本身 的一部分,而是 ...
- python常用执行方式&变量&input函数
linux系统中执行py文件方式: ./a.py 需要执行权限 chmod -R 777(最大权限) 常用执行方式: 1. ./a.py2. python a.py 文件内部头加上 #!/usr/b ...
- RAC集群数据库连库代码示例(jdbc thin方式,非oci)
1.RAC集群数据库连库代码示例(jdbc thin方式,非oci):jdbc.driverClassName=oracle.jdbc.driver.OracleDriverjdbc.url=jdbc ...
- Linux 常用分区方式
1 分两个区 主目录:/ 交换分区:swap 2 常用分区方式,以使用100G空间安装linux为例 引导分区: 挂载点/boot,分区格式ext4,500M以内即可 交换分区: 无挂载点,分区格式选 ...
随机推荐
- setScaledContents
ui->catchPhotoLabel_607->setPixmap(QPixmap::fromImage(*m_imageCatchtDefaultPhoto_607).scaled(Q ...
- CentOS7系统重置root密码
https://blog.csdn.net/qq_42969074/article/details/88080821
- POJ-3268(来回最短路+dijkstra算法)
Silver Cow Party POJ-3268 这题也是最短路的模板题,只不过需要进行两次求解最短路,因为涉及到来回的最短路之和. 该题的求解关键是:求解B-A的最短路时,可以看做A是起点,这就和 ...
- JPress企业站主题-zbout
JPress企业站主题-zbout 经典的黑白灰颜色搭配风格,首页配置有轮播图.案例展示.公司简介.新闻中心.联系方式以及合作伙伴模块,全站使用了响应式结构,可以自适应电脑端和手机端浏览器访问.主题整 ...
- BuaacodingT651 我知道你不知道圣诞节做什么 题解(逻辑)
题目链接 我知道你不知道圣诞节做什么 解题思路 第一句话:x,y不都为质数. 第二句话:对于xy=t,存在唯一一种x+y使得x,y不都为质数. 第三句话:对于x+y=s,存在唯一一种t=xy使得对于任 ...
- 【Django必备01】——什么是Django框架?有什么优势?模块组成介绍。
01.什么是Django框架? Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式.使用这种架构,程序员可以方便.快捷地创建高品质.易维护.数据库驱动的应用程序. ...
- css整理之-----------技巧、黑魔法
css 看起来比较简单,但是要想做的好也不是那么容易,我们在平时开发中,主要用css 来美化我们的html结构,所有我觉得css 还是挺重要的,这里记录整理一些关于css 的技巧以及容易忘记的知识点. ...
- POJ - 1163 The Triangle 【动态规划】
一.题目 The Triangle 二.分析 动态规划入门题. 状态转移方程$$DP[i][j] = A[i][j] + max(DP[i-1][j], DP[i][j])$$ 三.AC代码 1 #i ...
- Python中异步协程的使用方法介绍
1. 前言 在执行一些 IO 密集型任务的时候,程序常常会因为等待 IO 而阻塞.比如在网络爬虫中,如果我们使用 requests 库来进行请求的话,如果网站响应速度过慢,程序一直在等待网站响应,最后 ...
- 微信小程序应用开发-手动创建
基础知识: index.wxml的代码为 Html,有很多标签,如等 index.wwss相当于css 即样式 index.js中有很多函数,可自定义 操作步骤: 删除app.json文件中page/ ...