迁移基于Microsoft.DirectX的AudioRecoder类到SharpDX上
最近迁移项目到x64上,要处理的东西还是蛮多的,所以我要在说一次,不到万不得已不要用COM组件,要用COM组件也得首先考虑不需要我们关心平台的做法,或者得有64位版本。
比如Office的COM组件调用,excel可以用NPOI大家都知道了,如果你没用收费的aspose,那么你要操作其他office比如word,ppt等可以用NetOffice组件,虽然同样是调用COM,但是x86 x64都可以使用,而且任意完整本地Office版本即可(97~)
回到正题,这次是把基于 Microsoft.DirectX,Microsoft.DirectX.DirectSound这些32bit类库的AudioRecoder类迁移到SharpDX上来实现跨平台
引用
SharpDX
SharpDX.DirectSound
即可
///Record sound from MicroPhone and save to wav file // 迁移到SharpDX by bernard 2018.7.27
//参考:
//https://csharp.hotexamples.com/examples/SharpDX.DirectSound/NotificationPosition/-/php-notificationposition-class-examples.html
//http://daisy-trac.cvsdude.com/urakawa-sdk/browser/trunk/csharp/audio/AudioLib/AudioRecorder.cs?rev=2355 using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using SharpDX.DirectSound;
using SharpDX.Multimedia;
using SharpDX; namespace AudioLib
{
class Recorder
{
private const int NOTIFYNUMBER = ; // 缓冲队列的数目
private int mNextCaptureOffset = ; // 该次录音缓冲区的起始点
private int nSampleCount = ; // 录制的样本数目
private int nNotifySize = ; // 每次通知大小
private int nBufferSize = ; // 缓冲队列大小
private string m_strFileName = string.Empty; // 文件名
private FileStream fsWaveFile = null; // 文件流
private BinaryWriter bwWriter = null; // 写文件
private DirectSoundCapture CapDev = null; // 音频捕捉设备
private CaptureBuffer RecBuffer = null; // 缓冲区对象
private NotificationPosition Notify = null; // 消息通知对象
private WaveFormat WavFormat; // 录音的格式
private Thread NotifyThread = null; // 处理缓冲区消息的线程
private AutoResetEvent NotificationEvent = null; // 通知事件 public Recorder(string strFileName)
{
m_strFileName = strFileName;
// 初始化音频捕捉设备
InitCaptureDevice();
// 设定录音格式
WavFormat = CreateWaveFormat();
} /// <summary> /// 开始录音 /// </summary> public void RecStart()
{
try
{
// 创建录音文件 CreateSoundFile();
// 创建一个录音缓冲区,并开始录音 CreateCaptureBuffer(); // 建立通知消息,当缓冲区满的时候处理方法
InitNotifications(); RecBuffer.Start(true); }
catch (Exception ex)
{
throw new Exception(ex.Message);
} }
/// <summary> /// 停止录音 /// </summary> public void RecStop()
{
try
{
// 关闭通知消息 if (null != NotificationEvent)
{
NotificationEvent.Set(); }
// 停止录音 RecBuffer.Stop();
// 写入缓冲区最后的数据 RecordCapturedData(); // 回写长度信息 bwWriter.Seek(, SeekOrigin.Begin); bwWriter.Write((int)(nSampleCount + )); // 写文件长度 bwWriter.Seek(, SeekOrigin.Begin); bwWriter.Write(nSampleCount); // 写数据长度 bwWriter.Close(); fsWaveFile.Close(); bwWriter = null; fsWaveFile = null;
nSampleCount = ;
// 3. To Dispose the capture
CapDev.Dispose(); // 4. Null the capture
CapDev = null; // 5. To dispose the buffer
RecBuffer.Dispose(); // 6. To Null the buffer
RecBuffer = null; }
catch (Exception ex)
{
//throw new Exception(ex.Message);
System.Diagnostics.Debug.WriteLine(ex.Message);
}
} /// <summary>
/// 继续录音
/// </summary> public void RecContinue()
{
try
{
// 建立通知消息,当缓冲区满的时候处理方法
RecBuffer.Start(true); }
catch (Exception ex)
{
throw new Exception(ex.Message);
} }
/// <summary>
/// 暂停录音
/// </summary> public void RecPause()
{
try
{
// 关闭通知消息 if (null != NotificationEvent)
{
NotificationEvent.Set(); }
// 停止录音
RecBuffer.Stop(); }
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
/// <summary>
/// 初始化录音设备,此处使用主录音设备.
/// </summary>
public void InitCaptureDevice()
{
try
{
//// 获取默认音频捕捉设备 var devices = SharpDX.DirectSound.DirectSoundCapture.GetDevices(); // 枚举音频捕捉设备 Guid deviceGuid = Guid.Empty; // 音频捕捉设备的ID
if (devices.Count > )
{
deviceGuid = devices[].DriverGuid;
}
else
{
throw new Exception("Not any sound capture device"); }
//// 用指定的捕捉设备创建Capture对象 try
{
CapDev = new DirectSoundCapture();
} catch (SharpDXException e)
{ throw new Exception(e.ToString()); }
}
catch (Exception ex)
{
throw new Exception(ex.Message);
} } /// <summary>
/// 创建录音格式,此处使用16bit,16KHz,Mono的录音格式
/// </summary>
/// <returns>WaveFormat结构体</returns> private WaveFormat CreateWaveFormat()
{ try
{
WaveFormat format = new WaveFormat(, , );
return format;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
} } /// <summary>
/// 创建录音使用的缓冲区
/// </summary> private void CreateCaptureBuffer()
{ try
{
// 缓冲区的描述对象 CaptureBufferDescription bufferdescription = new CaptureBufferDescription();
if (null != Notify)
{ Notify = null; } if (null != RecBuffer)
{ RecBuffer.Dispose(); RecBuffer = null; } // 设定通知的大小,默认为1s钟
nNotifySize = ( > WavFormat.AverageBytesPerSecond / ) ? : (WavFormat.AverageBytesPerSecond / ); nNotifySize -= nNotifySize % WavFormat.BlockAlign;
// 设定缓冲区大小 nBufferSize = nNotifySize * NOTIFYNUMBER;
// 创建缓冲区描述 bufferdescription.BufferBytes = nBufferSize; bufferdescription.Format = WavFormat; // 录音格式
// 创建缓冲区 RecBuffer = new CaptureBuffer(CapDev, bufferdescription); mNextCaptureOffset = ;
}
catch (Exception ex)
{
throw new Exception("Create recorder object failure, check sound driver or contact with administrator");
} } /// <summary> /// 初始化通知事件,将原缓冲区分成16个缓冲队列,在每个缓冲队列的结束点设定通知点. /// </summary> /// <returns>是否成功</returns> private void InitNotifications()
{
try
{
if (null == RecBuffer)
{
throw new Exception("Not create sound record buffer"); }
// 创建一个通知事件,当缓冲队列满了就激发该事件. NotificationEvent = new AutoResetEvent(false);
// 创建一个线程管理缓冲区事件 if (null == NotifyThread)
{ NotifyThread = new Thread(new ThreadStart(WaitThread)); NotifyThread.Start(); }
// 设定通知的位置 NotificationPosition[] PositionNotify = new NotificationPosition[NOTIFYNUMBER]; for (int i = ; i < NOTIFYNUMBER; i++)
{
PositionNotify[i] = new NotificationPosition();
PositionNotify[i].Offset = (nNotifySize * i) + nNotifySize - ; PositionNotify[i].WaitHandle = NotificationEvent; }
//Notify = new NotificationPosition(RecBuffer); RecBuffer.SetNotificationPositions(PositionNotify);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
} }
/// <summary> /// 将录制的数据写入wav文件 /// </summary> private void RecordCapturedData()
{// 这里瞎改的,需要Review
try
{
byte[] CaptureData = null; int ReadPos; int CapturePos = RecBuffer.CurrentCapturePosition; ReadPos = RecBuffer.CurrentRealPosition;
int sizeBytes = RecBuffer.Capabilities.BufferBytes; int circularBufferBytesAvailableForReading = (CapturePos == mNextCaptureOffset ?
: (CapturePos < mNextCaptureOffset
? CapturePos + (sizeBytes - mNextCaptureOffset)
: CapturePos - mNextCaptureOffset)); circularBufferBytesAvailableForReading -= (circularBufferBytesAvailableForReading % (sizeBytes / NOTIFYNUMBER)); if (circularBufferBytesAvailableForReading == )
{
return;
}
// 读取缓冲区内的数据
CaptureData = new byte[circularBufferBytesAvailableForReading];
RecBuffer.Read(CaptureData, , circularBufferBytesAvailableForReading, mNextCaptureOffset, LockFlags.None);
// 写入Wav文件 bwWriter.Write(CaptureData, , CaptureData.Length);
// 更新已经录制的数据长度.
bwWriter.Flush();
nSampleCount += CaptureData.Length;
// 移动录制数据的起始点,通知消息只负责指示产生消息的位置,并不记录上次录制的位置 mNextCaptureOffset += CaptureData.Length; mNextCaptureOffset %= nBufferSize; // Circular buffer
}
catch (Exception ex)
{
//throw new Exception(ex.Message);
System.Diagnostics.Debug.WriteLine(ex.Message);
} } /// <summary> /// 接收缓冲区满消息的处理线程 /// </summary> private void WaitThread()
{ while (true)
{ // 等待缓冲区的通知消息
NotificationEvent.WaitOne(Timeout.Infinite, true);
// 录制数据 RecordCapturedData(); } } /// <summary> /// 创建保存的波形文件,并写入必要的文件头. /// </summary> private void CreateSoundFile()
{
try
{
/**************************************************************************
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.
***************************************************************************/
// Open up the wave file for writing. fsWaveFile = new FileStream(m_strFileName, FileMode.Create); bwWriter = new BinaryWriter(fsWaveFile);
// 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 = ; // File padding
int nFormatChunkLength = 0x10; // Format chunk length.
int nLength = ; // File length, minus first 8 bytes of RIFF description. This will be filled in later.
short shBytesPerSample = ; // Bytes per sample.
// 一个样本点的字节数目
if ( == WavFormat.BitsPerSample && == WavFormat.Channels)
{
shBytesPerSample = ;
} else if (( == WavFormat.BitsPerSample && == WavFormat.Channels) || ( == WavFormat.BitsPerSample && == WavFormat.Channels))
{
shBytesPerSample = ;
} else if ( == WavFormat.BitsPerSample && == WavFormat.Channels)
{
shBytesPerSample = ;
} // RIFF 块 bwWriter.Write(ChunkRiff); bwWriter.Write(nLength); bwWriter.Write(ChunkType);
// WAVE块 bwWriter.Write(ChunkFmt); bwWriter.Write(nFormatChunkLength); bwWriter.Write(shPad); bwWriter.Write((short)WavFormat.Channels); bwWriter.Write(WavFormat.SampleRate); bwWriter.Write(WavFormat.AverageBytesPerSecond); bwWriter.Write(shBytesPerSample); bwWriter.Write((short)WavFormat.BitsPerSample); // 数据块 bwWriter.Write(ChunkData); bwWriter.Write((int)); // The sample length will be written in later.
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
} }
}
迁移基于Microsoft.DirectX的AudioRecoder类到SharpDX上的更多相关文章
- 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj
原文 将 WPF.UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj 写过 .NET Standard 类库或者 .NET Core 程序的 ...
- 基于Microsoft Azure、ASP.NET Core和Docker的博客系统
欢迎阅读daxnet的新博客:一个基于Microsoft Azure.ASP.NET Core和Docker的博客系统 2008年11月,我在博客园开通了个人帐号,并在博客园发表了自己的第一篇博客 ...
- NET Core2.0 Memcached踩坑,基于EnyimMemcachedCore整理MemcachedHelper帮助类。
DotNetCore2.0下使用memcached缓存. Memcached目前微软暂未支持,暂只支持Redis,由于项目历史原因,先用博客园开源项目EnyimMemcachedCore,后续用到的时 ...
- Asp.Net Core 2.0 项目实战(5)Memcached踩坑,基于EnyimMemcachedCore整理MemcachedHelper帮助类。
Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...
- 并发编程概述 委托(delegate) 事件(event) .net core 2.0 event bus 一个简单的基于内存事件总线实现 .net core 基于NPOI 的excel导出类,支持自定义导出哪些字段 基于Ace Admin 的菜单栏实现 第五节:SignalR大杂烩(与MVC融合、全局的几个配置、跨域的应用、C/S程序充当Client和Server)
并发编程概述 前言 说实话,在我软件开发的头两年几乎不考虑并发编程,请求与响应把业务逻辑尽快完成一个星期的任务能两天完成绝不拖三天(剩下时间各种浪),根本不会考虑性能问题(能接受范围内).但随着工 ...
- 2018-12-6-Roslyn-如何基于-Microsoft.NET.Sdk-制作源代码包
title author date CreateTime categories Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包 lindexi 2018-12-06 16:2 ...
- 用Microsoft DirectX光线跟踪改善渲染质量
用Microsoft DirectX光线跟踪改善渲染质量 Implementing Stochastic Levels of Detail with Microsoft DirectX Raytrac ...
- [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏
前言 这款安卓小游戏是基于SurfaceView的飞行射击类游戏,采用Java来写,没有采用游戏引擎,注释详细,条理比较清晰,适合初学者了解游戏状态转化自动机和一些继承与封装的技巧. 效果展示 ...
- 运用Microsoft.DirectX.DirectSound和Microsoft.DirectX实现简单的录音功能
1.首先要使用Microsoft.DirectX.DirectSound和Microsoft.DirectX这两个dll进行录音,需要先安装microsoft directx 9.0cz这个组件, 百 ...
随机推荐
- application/json和application/x-www-form-urlencoded使用选择
一.参考资料 选application/x-www-form-urlencoded还是application/json? @RequestBody应用 二.理解 1.@RequestBody的作用 注 ...
- Python 多个分隔符 读取逗号和空格分开的数据
str.split() 清除默认 空格和tab 对空格数量不敏感 str.split(' ') 只清除一个空格 对空格数量敏感 l = re.split('[^0-9.]+',s.stri ...
- 消息模式Toast.makeText的几种常见用法
Toast 是一个 View 视图,快速的为用户显示少量的信息. Toast 在应用程序上浮动显示信息给用户,它永远不会获得焦点,不影响用户的输入等操作,主要用于 一些帮助 / 提示. Toast 最 ...
- Android单片机与蓝牙模块通信实例代码
Android单片机与蓝牙模块通信实例代码 参考路径:http://www.jb51.net/article/83349.htm 啦啦毕业了,毕业前要写毕业设计,需要写一个简单的蓝牙APP进行交互,通 ...
- 网页打印样式CSS
相信大多数的前端工程师都是处理显示屏上面的设计,用到最多的计量单位就是px,但是有些时候,我们难免也会有打印的需求,比如一个电商平台的“物流配送打印单”,“打印订单”等等可能都是需要从网友上打印出来的 ...
- async/await让你的代码更加优雅
一. 回调地狱:回调函数里面嵌套着回调函数嵌套着回调函数”,这就是被传说中的“回调地狱callbackHell () { const api = new Api() let user, friends ...
- Django用openLDAP做认证
前言 之前有需求要做一个django+ldap用户管理的简单接口,研究了好几个模块,最后终于能实现django用ldap做用户认证了.也是自己的水平有限吧,做了好长时间,现在就和大家分享一下这个过程吧 ...
- 一些有价值的Blog推荐
待看的一些文章 1. 性能调优攻略 http://coolshell.cn/articles/7490.html 2. 内存的存储管理--段式和页式管理的区别 http://blog.sina.com ...
- mybatis环境配置与入门例子
1.jar包的导入 mybatis需要jar包:mybatis-3.4.6.jar mysql驱动jar包:mysql-connector-java-5.1.34.-bin.jar 日志记录jar包: ...
- hotmail 收不到邮件的问题
之前用 hotmail 注册过一个 aws 账号,起了一个 ec2 的免费一年的 VPS,然后没怎么用,不久就把这事忘了. 直到有一天手机收到信用卡扣款消息,我就马上去登账号,却怎么也想不起密码来了, ...