代码语言
.
CSharp
.
JS
Java
Asp.Net
C
MSSQL
PHP
Css
PLSQL
Python
Shell
EBS
ASP
Perl
ObjC
VB.Net
VBS
MYSQL
GO
Delphi
AS
DB2
Domino
Rails
ActionScript
Scala
代码分类
文件
系统
字符串
数据库
网络相关
图形/GUI
多媒体
算法
游戏
Jquery
Extjs
Android
HTML5
菜单
网页交互
WinForm
控件
企业应用
安全与加密
脚本/批处理
开放平台
其它
【
C#
】
音频采集
作者:
壮壮
/ 发布于
2016/10/19
/
742
using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Threading; using Microsoft.DirectX; using Microsoft.DirectX.DirectSound; using System.Net.Sockets; using System.Net; namespace MatureVoice { class VoiceCapture { private MemoryStream memstream;//内存流 private SecondaryBuffer secBuffer;//辅助缓冲区 private int iNotifySize = 0;//通知大小 private int iBufferSize = 0;//捕捉缓冲区大小 private CaptureBuffer capturebuffer;//捕捉缓冲区对象 private AutoResetEvent notifyEvent;//通知事件 private Thread notifyThread;//通知线程 private int iNotifyNum=0;//通知个数 private Notify myNotify;//通知对象 private Capture capture;//捕捉设备对象 private Device PlayDev;//播放设备对象 private BufferDescription buffDiscript; private Socket Client; private EndPoint epServer; private int iBufferOffset=0;//捕捉缓冲区位移 private IntPtr intptr;//窗口句柄 public IntPtr Intptr { set { intptr = value; } } public int NotifySize { set { iNotifySize = value; } } public int NotifyNum { set { iNotifyNum = value; } } public Socket LocalSocket { set { Client = value; } } public EndPoint RemoteEndPoint { set { epServer = value; } } /// <summary> /// 初始化相关操作 /// </summary> public void InitVoice() {//初始化声音相关设置:(1)捕捉缓冲区(2)播放缓冲区 if (!CreateCaputerDevice()) { throw new Exception(); }//建立设备对象 CreateCaptureBuffer();//建立缓冲区对象 CreateNotification();//设置通知及事件 //======(2)============== if (!CreatePlayDevice()) { throw new Exception(); } CreateSecondaryBuffer(); } /// <summary> /// 启动声音采集 /// </summary> public void StartVoiceCapture() { capturebuffer.Start(true);//true表示设置缓冲区为循环方式,开始捕捉 } /// <summary> /// 创建用于播放的音频设备对象 /// </summary> /// <returns>创建成功返回true</returns> private bool CreatePlayDevice() { DevicesCollection dc = new DevicesCollection(); Guid g; if (dc.Count > 0) { g = dc[0].DriverGuid; } else { return false; } PlayDev = new Device(g); PlayDev.SetCooperativeLevel(intptr, CooperativeLevel.Normal); return true; } /// <summary> /// 创建辅助缓冲区 /// </summary> private void CreateSecondaryBuffer() { buffDiscript = new BufferDescription(); WaveFormat mWavFormat = SetWaveFormat(); buffDiscript.Format = mWavFormat; iNotifySize = mWavFormat.AverageBytesPerSecond / iNotifyNum;//设置通知大小 iBufferSize = iNotifyNum * iNotifySize; buffDiscript.BufferBytes = iBufferSize; buffDiscript.ControlPan = true; buffDiscript.ControlFrequency = true; buffDiscript.ControlVolume = true; buffDiscript.GlobalFocus = true; secBuffer = new SecondaryBuffer(buffDiscript, PlayDev); byte[] bytMemory = new byte[100000]; memstream = new MemoryStream(bytMemory, 0, 100000, true, true); //g729 = new G729(); //g729.InitalizeEncode(); //g729.InitalizeDecode(); } /// <summary> /// 创建捕捉设备对象 /// </summary> /// <returns>如果创建成功返回true</returns> private bool CreateCaputerDevice() { //首先要玫举可用的捕捉设备 CaptureDevicesCollection capturedev = new CaptureDevicesCollection(); Guid devguid; if (capturedev.Count > 0) { devguid = capturedev[0].DriverGuid; } else { System.Windows.Forms.MessageBox.Show("当前没有可用于音频捕捉的设备", "系统提示"); return false; } //利用设备GUID来建立一个捕捉设备对象 capture = new Capture(devguid); return true; } /// <summary> /// 创建捕捉缓冲区对象 /// </summary> private void CreateCaptureBuffer() { //想要创建一个捕捉缓冲区必须要两个参数:缓冲区信息(描述这个缓冲区中的格式等),缓冲设备。 WaveFormat mWavFormat = SetWaveFormat(); CaptureBufferDescription bufferdescription = new CaptureBufferDescription(); bufferdescription.Format = mWavFormat;//设置缓冲区要捕捉的数据格式 iNotifySize = mWavFormat.AverageBytesPerSecond / iNotifyNum;//1秒的数据量/设置的通知数得到的每个通知大小小于0.2s的数据量,话音延迟小于200ms为优质话音 iBufferSize = iNotifyNum * iNotifySize; bufferdescription.BufferBytes = iBufferSize; bufferdescription.ControlEffects = true; bufferdescription.WaveMapped = true; capturebuffer = new CaptureBuffer(bufferdescription, capture);//建立设备缓冲区对象 } //设置通知 private void CreateNotification() { BufferPositionNotify[] bpn = new BufferPositionNotify[iNotifyNum];//设置缓冲区通知个数 //设置通知事件 notifyEvent = new AutoResetEvent(false); notifyThread = new Thread(RecoData);//通知触发事件 notifyThread.IsBackground = true; notifyThread.Start(); for (int i = 0; i < iNotifyNum; i++) { bpn[i].Offset = iNotifySize + i * iNotifySize - 1;//设置具体每个的位置 bpn[i].EventNotifyHandle = notifyEvent.Handle; } myNotify = new Notify(capturebuffer); myNotify.SetNotificationPositions(bpn); } //线程中的事件 private void RecoData() { while (true) { // 等待缓冲区的通知消息 notifyEvent.WaitOne(Timeout.Infinite, true); // 录制数据 RecordCapturedData(Client,epServer); } } //真正转移数据的事件,其实就是把数据传送到网络上去。 private void RecordCapturedData(Socket Client,EndPoint epServer ) { byte[] capturedata = null; int readpos = 0, capturepos = 0, locksize = 0; capturebuffer.GetCurrentPosition(out capturepos, out readpos); locksize = readpos - iBufferOffset;//这个大小就是我们可以安全读取的大小 if (locksize == 0) { return; } if (locksize < 0) {//因为我们是循环的使用缓冲区,所以有一种情况下为负:当文以载读指针回到第一个通知点,而Ibuffeoffset还在最后一个通知处 locksize += iBufferSize; } capturedata = (byte[])capturebuffer.Read(iBufferOffset, typeof(byte), LockFlag.FromWriteCursor, locksize); //capturedata = g729.Encode(capturedata);//语音编码 try { Client.SendTo(capturedata, epServer);//传送语音 } catch { throw new Exception(); } iBufferOffset += capturedata.Length; iBufferOffset %= iBufferSize;//取模是因为缓冲区是循环的。 } private int intPosWrite = 0;//内存流中写指针位移 private int intPosPlay = 0;//内存流中播放指针位移 private int intNotifySize = 5000;//设置通知大小 /// <summary> /// 从字节数组中获取音频数据,并进行播放 /// </summary> /// <param name="intRecv">字节数组长度</param> /// <param name="bytRecv">包含音频数据的字节数组</param> public void GetVoiceData(int intRecv, byte[] bytRecv) { //intPosWrite指示最新的数据写好后的末尾。intPosPlay指示本次播放开始的位置。 if (intPosWrite + intRecv <= memstream.Capacity) {//如果当前写指针所在的位移+将要写入到缓冲区的长度小于缓冲区总大小 if ((intPosWrite - intPosPlay >= 0 && intPosWrite - intPosPlay < intNotifySize) || (intPosWrite - intPosPlay < 0 && intPosWrite - intPosPlay + memstream.Capacity < intNotifySize)) { memstream.Write(bytRecv, 0, intRecv); intPosWrite += intRecv; } else if (intPosWrite - intPosPlay >= 0) {//先存储一定量的数据,当达到一定数据量时就播放声音。 buffDiscript.BufferBytes = intPosWrite - intPosPlay;//缓冲区大小为播放指针到写指针之间的距离。 SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。 memstream.Position = intPosPlay;//先将memstream的指针定位到这一次播放开始的位置 sec.Write(0, memstream, intPosWrite - intPosPlay, LockFlag.FromWriteCursor); sec.Play(0, BufferPlayFlags.Default); memstream.Position = intPosWrite;//写完后重新将memstream的指针定位到将要写下去的位置。 intPosPlay = intPosWrite; } else if (intPosWrite - intPosPlay < 0) { buffDiscript.BufferBytes = intPosWrite - intPosPlay + memstream.Capacity;//缓冲区大小为播放指针到写指针之间的距离。 SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。 memstream.Position = intPosPlay; sec.Write(0, memstream, memstream.Capacity - intPosPlay, LockFlag.FromWriteCursor); memstream.Position = 0; sec.Write(memstream.Capacity - intPosPlay, memstream, intPosWrite, LockFlag.FromWriteCursor); sec.Play(0, BufferPlayFlags.Default); memstream.Position = intPosWrite; intPosPlay = intPosWrite; } } else {//当数据将要大于memstream可容纳的大小时 int irest = memstream.Capacity - intPosWrite;//memstream中剩下的可容纳的字节数。 memstream.Write(bytRecv, 0, irest);//先写完这个内存流。 memstream.Position = 0;//然后让新的数据从memstream的0位置开始记录 memstream.Write(bytRecv, irest, intRecv - irest);//覆盖旧的数据 intPosWrite = intRecv - irest;//更新写指针位置。写指针指示下一个开始写入的位置而不是上一次结束的位置,因此不用减一 } } /// <summary> /// 设置音频格式,如采样率等 /// </summary> /// <returns>设置完成后的格式</returns> private WaveFormat SetWaveFormat() { WaveFormat format = new WaveFormat(); format.FormatTag = WaveFormatTag.Pcm;//设置音频类型 format.SamplesPerSecond = 11025;//采样率(单位:赫兹)典型值:11025、22050、44100Hz format.BitsPerSample = 16;//采样位数 format.Channels = 1;//声道 format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));//单位采样点的字节数 format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond; return format; //按照以上采样规格,可知采样1秒钟的字节数为22050*2=44100B 约为 43K } /// <summary> /// 停止语音采集 /// </summary> public void Stop() { capturebuffer.Stop(); if (notifyEvent != null) { notifyEvent.Set(); } if (notifyThread != null && notifyThread.IsAlive == true) { notifyThread.Abort(); } } }
试试其它关键字
同语言下
.
C#实现的html内容截取
.
C#动态修改文件夹名称(FSO实现,不移动文件)
.
c# 移动文件或文件夹
.
c#图片添加水印
.
网站后台修改图片尺寸代码
.
处理大图片在缩略图时的展示
.
实现对图片上传的接收
.
去除字符串中的空格,回车,换行符转变成‘;’在按‘
.
按照回车换行符分割字符串
.
文件MD5码 比较,检测文件是否一样
可能有用的
.
C#实现的html内容截取
.
C#动态修改文件夹名称(FSO实现,不移动文件)
.
c# 移动文件或文件夹
.
c#图片添加水印
.
网站后台修改图片尺寸代码
.
处理大图片在缩略图时的展示
.
实现对图片上传的接收
.
去除字符串中的空格,回车,换行符转变成‘;’在按‘
.
按照回车换行符分割字符串
.
文件MD5码 比较,检测文件是否一样
壮壮
贡献的其它代码
(
8
)
.
mybatis如何遍历Map的key和value
.
udf使用间隔
.
获取 HTTP 服务器的信息
.
提取 HTML 文档中的链接和图片
.
网络相关
.
音频采集
.
字符串字串判断
.
模式匹配(kmp)
Copyright © 2004 - 2024 dezai.cn. All Rights Reserved
站长博客
粤ICP备13059550号-3