C#中使用WavHelper保存录音数据为wav文件
时间:2021-08-11
本文章向大家介绍C#中使用WavHelper保存录音数据为wav文件,主要包括C#中使用WavHelper保存录音数据为wav文件使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
C#将录音数据文件保存为wav格式文件,这里使用到的是WavHelper工具类。
WavHelper工具类:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WavHelperTool { /// <summary> /// 生成wav文件的帮助类,本类来自网络,作者不详 /// </summary> public class WavHelper : IDisposable { #region 变量 private MyWaveFormat mWavFormat; private int mSampleCount = 0; private string mFileName = ""; private FileStream mWaveFile = null; private BinaryWriter mWriter = null; private int _sampleRate = 0; private short _channels = 0; private string _filePath = ""; #endregion public WavHelper(string audioFileName, int sampleRate, short channels) { _filePath = audioFileName; _sampleRate = sampleRate; _channels = channels; this.mFileName = audioFileName; mWavFormat = new MyWaveFormat(); mWavFormat.SamplesPerSecond = _sampleRate; mWavFormat.BitsPerSample = 16; mWavFormat.Channels = _channels; mWavFormat.BlockAlign = (short)(mWavFormat.Channels * (mWavFormat.BitsPerSample / 8)); mWavFormat.AverageBytesPerSecond = mWavFormat.BlockAlign * mWavFormat.SamplesPerSecond; Start(); } /// <summary> /// 存放的录音文件路径 /// </summary> public string FilePath { get { return _filePath; } } public void Dispose() { Stop(); } /// <summary> /// 结束写入并关闭文件 /// </summary> private void Stop() { // 写WAV文件尾 mWriter.Seek(4, SeekOrigin.Begin); mWriter.Write((int)(mSampleCount + 36)); mWriter.Seek(40, SeekOrigin.Begin); mWriter.Write(mSampleCount); mWriter.Close(); mWaveFile.Close(); mWriter = null; mWaveFile = null; } /// <summary> /// 创建保存的波形文件,并写入必要的文件头. /// </summary> private void Start() { // Open up the wave file for writing. mWaveFile = new FileStream(mFileName, FileMode.Create); mWriter = new BinaryWriter(mWaveFile); /************************************************************************** Here is where the file will be created. A wave file is a RIFF file, which has chunks of data that describe what the file contains. A wave RIFF file is put together like this: The 12 byte RIFF chunk is constructed like this: Bytes 0 - 3 : 'R' 'I' 'F' 'F' Bytes 4 - 7 : Length of file, minus the first 8 bytes of the RIFF description. (4 bytes for "WAVE" + 24 bytes for format chunk length + 8 bytes for data chunk description + actual sample data size.) Bytes 8 - 11: 'W' 'A' 'V' 'E' The 24 byte FORMAT chunk is constructed like this: Bytes 0 - 3 : 'f' 'm' 't' ' ' Bytes 4 - 7 : The format chunk length. This is always 16. Bytes 8 - 9 : File padding. Always 1. Bytes 10- 11: Number of channels. Either 1 for mono, or 2 for stereo. Bytes 12- 15: Sample rate. Bytes 16- 19: Number of bytes per second. Bytes 20- 21: Bytes per sample. 1 for 8 bit mono, 2 for 8 bit stereo or 16 bit mono, 4 for 16 bit stereo. Bytes 22- 23: Number of bits per sample. The DATA chunk is constructed like this: Bytes 0 - 3 : 'd' 'a' 't' 'a' Bytes 4 - 7 : Length of data, in bytes. Bytes 8 -: Actual sample data. ***************************************************************************/ // Set up file with RIFF chunk info. char[] ChunkRiff = { 'R', 'I', 'F', 'F' }; char[] ChunkType = { 'W', 'A', 'V', 'E' }; char[] ChunkFmt = { 'f', 'm', 't', ' ' }; char[] ChunkData = { 'd', 'a', 't', 'a' }; short shPad = 1; // File padding int nFormatChunkLength = 0x10; // Format chunk length. int nLength = 0; // File length, minus first 8 bytes of RIFF description. This will be filled in later. short shBytesPerSample = 0; // Bytes per sample. // 一个样本点的字节数目 if (8 == mWavFormat.BitsPerSample && 1 == mWavFormat.Channels) shBytesPerSample = 1; else if ((8 == mWavFormat.BitsPerSample && 2 == mWavFormat.Channels) || (16 == mWavFormat.BitsPerSample && 1 == mWavFormat.Channels)) shBytesPerSample = 2; else if (16 == mWavFormat.BitsPerSample && 2 == mWavFormat.Channels) shBytesPerSample = 4; // RIFF 块 mWriter.Write(ChunkRiff); mWriter.Write(nLength); mWriter.Write(ChunkType); // WAVE块 mWriter.Write(ChunkFmt); mWriter.Write(nFormatChunkLength); mWriter.Write(shPad); mWriter.Write(mWavFormat.Channels); mWriter.Write(mWavFormat.SamplesPerSecond); mWriter.Write(mWavFormat.AverageBytesPerSecond); mWriter.Write(shBytesPerSample); mWriter.Write(mWavFormat.BitsPerSample); // 数据块 mWriter.Write(ChunkData); mWriter.Write((int)0); // The sample length will be written in later. } /// <summary> /// 每当采集到音频数据时,将其写入 /// </summary> /// <param name="data"></param> public void WriteAudioData(byte[] data) { mWriter.Write(data, 0, data.Length); mSampleCount += data.Length; } } public class MyWaveFormat { /// <summary> /// 采样率 /// </summary> public int SamplesPerSecond; /// <summary> /// 采样位数 /// </summary> public short BitsPerSample; /// <summary> /// 通道数 /// </summary> public short Channels; /// <summary> /// 单位采样点的字节数 /// </summary> public short BlockAlign; /// <summary> /// 每秒平均码率 /// </summary> public int AverageBytesPerSecond; } }
C#保存调用:
//1、定义wav文件生成类 private WavHelper microphoneWav = null; //2、初始化麦克风数据wav文件写入器 string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "microphone.wav");//根目录下生成microphone.wav microphoneWav = new WavHelper(path, sampleRate, channelCount);//保存路径、采样率、频道数 //3、数据写入wav文件中 byte[] data = "xxx";//比特数据 microphoneWav.WriteAudioData(data); //4、关闭写入 microphoneWav.Dispose(); microphoneWav = null;
总结:实践是检验真理的唯一标准。
原文地址:https://www.cnblogs.com/qiantao/p/15126990.html
- 《Redis设计与实现》读书笔记(三十二) ——Redis事务设计与实现
- 收藏一个简洁的PHP可逆加密函数
- 《Redis设计与实现》读书笔记(三十二) ——Redis集发布订阅设计与实现
- Android点击EditText文本框之外任何地方隐藏键盘的解决办法
- Spark 1.4为DataFrame新增的统计与数学函数
- 《Redis设计与实现》读书笔记(三十三) ——Redis排序命令sort的实现
- 高通HAL层之Sensor HAL
- Android Data Binding(数据绑定)用户指南
- Android 禁止Viewpager左右滑动功能
- 高通Audio中ASOC的machine驱动
- 《Redis设计与实现》读书笔记(三十五) ——Redis 二进制位数组及SWAR汉明重量算法
- Android TabWidget底部显示
- 《Redis设计与实现》读书笔记(三十六) ——Redis 慢查询日志实现
- 概率学中的随机变量与分布
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 案例:记录一则强制开库遭遇ORA-16433的处理过程
- mybatis升级为mybatis-plus踩到的坑
- Treepath
- linux 远程ssh免密登录
- npm 安装 electron taobao镜像 404错误 自用 实践笔记
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(二):部署
- 队列的一种实现:循环队列
- StackExchange.Redis .net core Timeout performing 超时问题
- G1 垃圾回收器简单调优
- Docker安装官方Redis镜像并启用密码认证 实践笔记
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(四):发布与回滚
- linux centos 安装Jenkins(非docker方式)
- mysql5.7 derived_merge=on 弄丢了我的 order by!
- 用nrm轻松管理npm镜像 自用 实践笔记
- 模拟战役(DFS||并查集解法)