最近迁移项目到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上的更多相关文章

  1. 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj

    原文 将 WPF.UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj 写过 .NET Standard 类库或者 .NET Core 程序的 ...

  2. 基于Microsoft Azure、ASP.NET Core和Docker的博客系统

    欢迎阅读daxnet的新博客:一个基于Microsoft Azure.ASP.NET Core和Docker的博客系统   2008年11月,我在博客园开通了个人帐号,并在博客园发表了自己的第一篇博客 ...

  3. NET Core2.0 Memcached踩坑,基于EnyimMemcachedCore整理MemcachedHelper帮助类。

    DotNetCore2.0下使用memcached缓存. Memcached目前微软暂未支持,暂只支持Redis,由于项目历史原因,先用博客园开源项目EnyimMemcachedCore,后续用到的时 ...

  4. 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 ...

  5. 并发编程概述 委托(delegate) 事件(event) .net core 2.0 event bus 一个简单的基于内存事件总线实现 .net core 基于NPOI 的excel导出类,支持自定义导出哪些字段 基于Ace Admin 的菜单栏实现 第五节:SignalR大杂烩(与MVC融合、全局的几个配置、跨域的应用、C/S程序充当Client和Server)

    并发编程概述   前言 说实话,在我软件开发的头两年几乎不考虑并发编程,请求与响应把业务逻辑尽快完成一个星期的任务能两天完成绝不拖三天(剩下时间各种浪),根本不会考虑性能问题(能接受范围内).但随着工 ...

  6. 2018-12-6-Roslyn-如何基于-Microsoft.NET.Sdk-制作源代码包

    title author date CreateTime categories Roslyn 如何基于 Microsoft.NET.Sdk 制作源代码包 lindexi 2018-12-06 16:2 ...

  7. 用Microsoft DirectX光线跟踪改善渲染质量

    用Microsoft DirectX光线跟踪改善渲染质量 Implementing Stochastic Levels of Detail with Microsoft DirectX Raytrac ...

  8. [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏

    前言  这款安卓小游戏是基于SurfaceView的飞行射击类游戏,采用Java来写,没有采用游戏引擎,注释详细,条理比较清晰,适合初学者了解游戏状态转化自动机和一些继承与封装的技巧. 效果展示    ...

  9. 运用Microsoft.DirectX.DirectSound和Microsoft.DirectX实现简单的录音功能

    1.首先要使用Microsoft.DirectX.DirectSound和Microsoft.DirectX这两个dll进行录音,需要先安装microsoft directx 9.0cz这个组件, 百 ...

随机推荐

  1. 51nod 1344

    一个很简单的算法题,求最小的前缀和,就是要注意数据范围要开一个longlong #include<iostream> using namespace std; int main() { i ...

  2. node.js获取参数的常用方法

    1.req.body 2.req.query 3.req.params 一.req.body例子 body不是nodejs默认提供的,你需要载入body-parser中间件才可以使用req.body, ...

  3. SimpleCursorAdapter和ListView的结合使用

    我们在用SQLite查数据的时候,经常会用到Cursor这个游标,我们希望能将游标指向的数据直接绑定到ListView中,这样就免去了将游标数据取出然后转换到SimpleAdapter中的麻烦.今天我 ...

  4. DSP builder安装指南(以9.1为例) 转自http://www.cnblogs.com/sleepy/archive/2011/06/28/2092362.html

    DSP Builder在算法友好的开发环境中帮助设计人员生成DSP设计硬件表征,从而缩短了DSP设计周期.已有的MATLAB函数和Simulink模块可以和Altera DSP Builder模块以及 ...

  5. Vuejs——(7)过渡(动画)

    版权声明:出处http://blog.csdn.net/qq20004604   目录(?)[+]   本篇资料来于官方文档: http://cn.vuejs.org/guide/transition ...

  6. 剑指offer编程题Java实现——面试题9斐波那契数列

    题目:写一个函数,输入n,求斐波那契数列的第n项. package Solution; /** * 剑指offer面试题9:斐波那契数列 * 题目:写一个函数,输入n,求斐波那契数列的第n项. * 0 ...

  7. 833. Find And Replace in String

    To some string S, we will perform some replacement operations that replace groups of letters with ne ...

  8. Javascript高级编程学习笔记(9)—— 执行环境

    今天主要讲一下,JS底层的一些东西,这些东西不太好举例(应该是我水平不够) 望大家多多海涵,比心心 执行环境 执行环境(执行上下文,全文使用执行环境 )是JS中最为重要的一个概念,执行环境决定了,变量 ...

  9. node.js使用redis储存session(详细步骤)

    转储session的原因 网上有许多session需要用数据库储存的原因,对我来说原因很简单,仅仅只是node的生产环境不允许将session存到服务器的内存中.会报一个内存溢出的风险警告.所以我决定 ...

  10. 仿B站项目——(2)环境配置,文件目录

    环境配置 主要参考入门Webpack,看这篇就够了,webpack入门和webpack实用配置. 实用开发环境 利用下面的webpack.json和webpack.config.js可以搭建一个使用e ...