迁移基于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这个组件, 百 ...
随机推荐
- Firefox录制时浏览器提示代理服务器拒绝连接
解决方法:检查火狐浏览器的代理设置是否正确,在 菜单栏 工具->选项->高级->网络->连接->设置里.将“配置访问因特网的代理”选项改为“无代理”.
- Django haystack+solr搜索引擎部署的坑.
跟着<<Django by Example>> 一路做下来,到了搭建搜索引擎的步骤 默认的思路是用 obj.objects.filter(body__icontains='fr ...
- centos7下载
http://archive.kernel.org/centos-vault/7.0.1406/isos/x86_64/
- kbmmw 5.07 正式发布
来了来了 5.07.00 Dec 9 2018 Important notes (changes that may break existing code) === ...
- 2019.03.01 bzoj3075: [Usaco2013]Necklace(kmp+dp)
传送门 题意简述:给出S,TS,TS,T两个字串,∣S∣≤10000,∣T∣≤1000|S|\le10000,|T|\le1000∣S∣≤10000,∣T∣≤1000,问至少从SSS中删去几个字符能够 ...
- 2019.02.15 bzoj5210: 最大连通子块和(链分治+ddp)
传送门 题意:支持单点修改,维护子树里的最大连通子块和. 思路: 扯皮: bzojbzojbzoj卡常差评. 网上的题解大多用了跟什么最大子段和一样的转移方法. 但是我们实际上是可以用矩阵转移的传统d ...
- 源码管理工具Git-客户端GitBash常用命令
1.配置用户名和邮箱地址(第一次启动程序时配置,以后使用不用配置)git config --global user.name "dolen"git config --global ...
- telnet的安装和使用
在日常使用中,有时候需要检测服务器上面的部分端口有没有打开,这个时候可以使用telnet进行调试.下面是一篇转载的文章. 原文地址:http://linuxtech.blog.51cto.com/36 ...
- JAVA 8 主要新特性 ----------------(二)JDK1.8优点概括
一.JDK1.8优点概括 1.速度更快 由于底层结构和JVM的改变,使得JDK1.8的速度提高. 2.代码更少(增加了新的语法 Lambda 表达式) 增加新特性Lambda表达式的 ...
- clickhouse安装使用文档
Clickhouse简介 Clickhouse是什么 1. 开源的列存储数据库管理系统 2. 支持线性扩展 3. 简单方便 4. 高可靠性 5. 容错(支持多主机异步复制,可以跨多个数据中心部署. 单 ...