迁移基于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

即可

 

  1 ///Record sound from MicroPhone and save to wav file
  2 
  3 // 迁移到SharpDX   by bernard  2018.7.27
  4 //参考:
  5 //https://csharp.hotexamples.com/examples/SharpDX.DirectSound/NotificationPosition/-/php-notificationposition-class-examples.html
  6 //http://daisy-trac.cvsdude.com/urakawa-sdk/browser/trunk/csharp/audio/AudioLib/AudioRecorder.cs?rev=2355
  7 
  8 
  9 using System;
 10 using System.Collections.Generic;
 11 using System.Text;
 12 using System.IO;
 13 using System.Threading;
 14 using System.Windows.Forms;
 15 using SharpDX.DirectSound;
 16 using SharpDX.Multimedia;
 17 using SharpDX;
 18 
 19 namespace AudioLib
 20 {
 21     class Recorder
 22     {
 23         private const int NOTIFYNUMBER = 16;       // 缓冲队列的数目
 24         private int mNextCaptureOffset = 0;      // 该次录音缓冲区的起始点
 25         private int nSampleCount = 0;            // 录制的样本数目
 26         private int nNotifySize = 0;             // 每次通知大小 
 27         private int nBufferSize = 0;             // 缓冲队列大小
 28         private string m_strFileName = string.Empty;     // 文件名 
 29         private FileStream fsWaveFile = null;         // 文件流 
 30         private BinaryWriter bwWriter = null;         // 写文件
 31         private DirectSoundCapture CapDev = null;              // 音频捕捉设备 
 32         private CaptureBuffer RecBuffer = null;     // 缓冲区对象 
 33         private NotificationPosition Notify = null;               // 消息通知对象
 34         private WaveFormat WavFormat;                       // 录音的格式 
 35         private Thread NotifyThread = null;                 // 处理缓冲区消息的线程 
 36         private AutoResetEvent NotificationEvent = null;    // 通知事件 
 37 
 38         public Recorder(string strFileName)
 39         {
 40             m_strFileName = strFileName;
 41             // 初始化音频捕捉设备 
 42             InitCaptureDevice();
 43             // 设定录音格式 
 44             WavFormat = CreateWaveFormat();
 45         }
 46 
 47         /// <summary> 
 48 
 49         /// 开始录音 
 50 
 51         /// </summary> 
 52 
 53         public void RecStart()
 54         {
 55             try
 56             {
 57                 // 创建录音文件 
 58 
 59                 CreateSoundFile();
 60                 // 创建一个录音缓冲区,并开始录音 
 61 
 62                 CreateCaptureBuffer();
 63 
 64                 // 建立通知消息,当缓冲区满的时候处理方法 
 65                 InitNotifications();
 66 
 67                 RecBuffer.Start(true);
 68 
 69 
 70 
 71             }
 72             catch (Exception ex)
 73             {
 74                 throw new Exception(ex.Message);
 75             }
 76 
 77         }
 78         /// <summary> 
 79 
 80         /// 停止录音 
 81 
 82         /// </summary>
 83 
 84         public void RecStop()
 85         {
 86             try
 87             {
 88                 // 关闭通知消息 
 89 
 90                 if (null != NotificationEvent)
 91                 {
 92                     NotificationEvent.Set();
 93 
 94                 }
 95                 // 停止录音 
 96 
 97                 RecBuffer.Stop();
 98                 // 写入缓冲区最后的数据 
 99 
100                 RecordCapturedData();
101 
102                 // 回写长度信息 
103 
104                 bwWriter.Seek(4, SeekOrigin.Begin);
105 
106                 bwWriter.Write((int)(nSampleCount + 36));   // 写文件长度 
107 
108                 bwWriter.Seek(40, SeekOrigin.Begin);
109 
110                 bwWriter.Write(nSampleCount);                // 写数据长度 
111 
112                 bwWriter.Close();
113 
114                 fsWaveFile.Close();
115 
116                 bwWriter = null;
117 
118                 fsWaveFile = null;
119                 nSampleCount = 0;
120                 // 3. To Dispose the capture
121                 CapDev.Dispose();
122 
123                 // 4. Null the capture
124                 CapDev = null;
125 
126                 // 5. To dispose the buffer
127                 RecBuffer.Dispose();
128 
129                 // 6. To Null the buffer
130                 RecBuffer = null;
131 
132 
133             }
134             catch (Exception ex)
135             {
136                 //throw new Exception(ex.Message);
137                 System.Diagnostics.Debug.WriteLine(ex.Message);
138             }
139         }
140 
141 
142 
143         /// <summary> 
144         /// 继续录音 
145         /// </summary>
146 
147         public void RecContinue()
148         {
149             try
150             {
151                 // 建立通知消息,当缓冲区满的时候处理方法                 
152                 RecBuffer.Start(true);
153 
154             }
155             catch (Exception ex)
156             {
157                 throw new Exception(ex.Message);
158             }
159 
160         }
161         /// <summary> 
162         /// 暂停录音 
163         /// </summary>
164 
165         public void RecPause()
166         {
167             try
168             {
169                 // 关闭通知消息 
170 
171                 if (null != NotificationEvent)
172                 {
173                     NotificationEvent.Set();
174 
175                 }
176                 // 停止录音 
177                 RecBuffer.Stop();
178 
179             }
180             catch (Exception ex)
181             {
182                 throw new Exception(ex.Message);
183             }
184         }
185         /// <summary> 
186         /// 初始化录音设备,此处使用主录音设备. 
187         /// </summary>         
188         public void InitCaptureDevice()
189         {
190             try
191             {
192                 //// 获取默认音频捕捉设备 
193 
194                 var devices = SharpDX.DirectSound.DirectSoundCapture.GetDevices();  // 枚举音频捕捉设备 
195 
196                 Guid deviceGuid = Guid.Empty;                                       // 音频捕捉设备的ID
197                 if (devices.Count > 0)
198                 {
199                     deviceGuid = devices[0].DriverGuid;
200                 }
201                 else
202                 {
203                     throw new Exception("Not any sound capture device");
204 
205                 }
206                 //// 用指定的捕捉设备创建Capture对象 
207 
208                 try
209                 {
210                     CapDev = new DirectSoundCapture();
211                 }
212 
213                 catch (SharpDXException e)
214                 {
215 
216                     throw new Exception(e.ToString());
217 
218                 }
219             }
220             catch (Exception ex)
221             {
222                 throw new Exception(ex.Message);
223             }
224 
225         }
226 
227         /// <summary> 
228         /// 创建录音格式,此处使用16bit,16KHz,Mono的录音格式
229         /// </summary> 
230         /// <returns>WaveFormat结构体</returns> 
231 
232         private WaveFormat CreateWaveFormat()
233         {
234 
235 
236             try
237             {
238                 WaveFormat format = new WaveFormat(16000, 16, 1);
239                 return format;
240             }
241             catch (Exception ex)
242             {
243                 throw new Exception(ex.Message);
244             }
245 
246         }
247 
248 
249         /// <summary> 
250         /// 创建录音使用的缓冲区
251         /// </summary> 
252 
253         private void CreateCaptureBuffer()
254         {
255 
256             try
257             {
258                 // 缓冲区的描述对象 
259 
260                 CaptureBufferDescription bufferdescription = new CaptureBufferDescription();
261                 if (null != Notify)
262                 {
263 
264                     Notify = null;
265 
266                 }
267 
268                 if (null != RecBuffer)
269                 {
270 
271                     RecBuffer.Dispose();
272 
273                     RecBuffer = null;
274 
275                 }
276 
277                 // 设定通知的大小,默认为1s钟 
278                 nNotifySize = (1024 > WavFormat.AverageBytesPerSecond / 8) ? 1024 : (WavFormat.AverageBytesPerSecond / 8);
279 
280                 nNotifySize -= nNotifySize % WavFormat.BlockAlign;
281                 // 设定缓冲区大小 
282 
283                 nBufferSize = nNotifySize * NOTIFYNUMBER;
284                 // 创建缓冲区描述 
285 
286                 bufferdescription.BufferBytes = nBufferSize;
287 
288                 bufferdescription.Format = WavFormat;           // 录音格式
289                                                                 // 创建缓冲区 
290 
291 
292                 RecBuffer = new CaptureBuffer(CapDev, bufferdescription);
293 
294                 mNextCaptureOffset = 0;
295             }
296             catch (Exception ex)
297             {
298                 throw new Exception("Create recorder object failure, check sound driver or contact with administrator");
299             }
300 
301         }
302 
303         /// <summary>
304 
305         /// 初始化通知事件,将原缓冲区分成16个缓冲队列,在每个缓冲队列的结束点设定通知点.
306 
307         /// </summary> 
308 
309         /// <returns>是否成功</returns> 
310 
311         private void InitNotifications()
312         {
313             try
314             {
315                 if (null == RecBuffer)
316                 {
317                     throw new Exception("Not create sound record buffer");
318 
319                 }
320                 // 创建一个通知事件,当缓冲队列满了就激发该事件. 
321 
322                 NotificationEvent = new AutoResetEvent(false);
323                 // 创建一个线程管理缓冲区事件 
324 
325                 if (null == NotifyThread)
326                 {
327 
328                     NotifyThread = new Thread(new ThreadStart(WaitThread));
329 
330                     NotifyThread.Start();
331 
332                 }
333                 // 设定通知的位置 
334 
335                 NotificationPosition[] PositionNotify = new NotificationPosition[NOTIFYNUMBER];
336 
337                 for (int i = 0; i < NOTIFYNUMBER; i++)
338                 {
339                     PositionNotify[i] = new NotificationPosition();
340                     PositionNotify[i].Offset = (nNotifySize * i) + nNotifySize - 1;
341 
342                     PositionNotify[i].WaitHandle = NotificationEvent;
343 
344                 }
345                 //Notify = new NotificationPosition(RecBuffer);
346 
347                 RecBuffer.SetNotificationPositions(PositionNotify);
348             }
349             catch (Exception ex)
350             {
351                 throw new Exception(ex.Message);
352             }
353 
354         }
355         /// <summary> 
356 
357         /// 将录制的数据写入wav文件 
358 
359         /// </summary> 
360 
361         private void RecordCapturedData()
362         {// 这里瞎改的,需要Review
363             try
364             {
365                 byte[] CaptureData = null;
366 
367                 int ReadPos;
368 
369                 int CapturePos = RecBuffer.CurrentCapturePosition;
370 
371                 ReadPos = RecBuffer.CurrentRealPosition;
372                 int sizeBytes = RecBuffer.Capabilities.BufferBytes;
373 
374                 int circularBufferBytesAvailableForReading = (CapturePos == mNextCaptureOffset ? 0
375                                   : (CapturePos < mNextCaptureOffset
376                             ? CapturePos + (sizeBytes - mNextCaptureOffset)
377                             : CapturePos - mNextCaptureOffset));
378 
379                 circularBufferBytesAvailableForReading -= (circularBufferBytesAvailableForReading % (sizeBytes / NOTIFYNUMBER));
380 
381                 if (circularBufferBytesAvailableForReading == 0)
382                 {
383                     return;
384                 }
385                 // 读取缓冲区内的数据 
386                 CaptureData = new byte[circularBufferBytesAvailableForReading];
387                 RecBuffer.Read(CaptureData, 0, circularBufferBytesAvailableForReading, mNextCaptureOffset, LockFlags.None);
388                 // 写入Wav文件
389 
390                 bwWriter.Write(CaptureData, 0, CaptureData.Length);
391                 // 更新已经录制的数据长度. 
392                 bwWriter.Flush();
393                 nSampleCount += CaptureData.Length;
394                 // 移动录制数据的起始点,通知消息只负责指示产生消息的位置,并不记录上次录制的位置 
395 
396                 mNextCaptureOffset += CaptureData.Length;
397 
398                 mNextCaptureOffset %= nBufferSize; // Circular buffer
399             }
400             catch (Exception ex)
401             {
402                 //throw new Exception(ex.Message);
403                 System.Diagnostics.Debug.WriteLine(ex.Message);
404             }
405 
406         }
407 
408         /// <summary> 
409 
410         /// 接收缓冲区满消息的处理线程 
411 
412         /// </summary> 
413 
414         private void WaitThread()
415         {
416 
417             while (true)
418             {
419 
420                 // 等待缓冲区的通知消息 
421                 NotificationEvent.WaitOne(Timeout.Infinite, true);
422                 // 录制数据 
423 
424                 RecordCapturedData();
425 
426             }
427 
428         }
429 
430         /// <summary> 
431 
432         /// 创建保存的波形文件,并写入必要的文件头. 
433 
434         /// </summary> 
435 
436         private void CreateSoundFile()
437         {
438             try
439             {
440                 /************************************************************************** 
441                 Here is where the file will be created. A wave file is a RIFF file, which has chunks 
442                 of data that describe what the file contains. A wave RIFF file is put together like this:
443                 The 12 byte RIFF chunk is constructed like this: 
444                 Bytes 0 - 3 :  'R' 'I' 'F' 'F'
445                 Bytes 4 - 7 :  Length of file, minus the first 8 bytes of the RIFF description. 
446                                (4 bytes for "WAVE" + 24 bytes for format chunk length + 
447                                8 bytes for data chunk description + actual sample data size.) 
448                 Bytes 8 - 11: 'W' 'A' 'V' 'E'
449                 The 24 byte FORMAT chunk is constructed like this: 
450                 Bytes 0 - 3 : 'f' 'm' 't' ' ' 
451                 Bytes 4 - 7 : The format chunk length. This is always 16.
452                 Bytes 8 - 9 : File padding. Always 1.
453                 Bytes 10- 11: Number of channels. Either 1 for mono,  or 2 for stereo. 
454                 Bytes 12- 15: Sample rate. 
455                 Bytes 16- 19: Number of bytes per second. 
456                 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. 
457                 Bytes 22- 23: Number of bits per sample.
458                 The DATA chunk is constructed like this: 
459                 Bytes 0 - 3 : 'd' 'a' 't' 'a' 
460                 Bytes 4 - 7 : Length of data, in bytes. 
461                 Bytes 8 -...: Actual sample data. 
462                 ***************************************************************************/
463                 // Open up the wave file for writing. 
464 
465                 fsWaveFile = new FileStream(m_strFileName, FileMode.Create);
466 
467                 bwWriter = new BinaryWriter(fsWaveFile);
468                 // Set up file with RIFF chunk info. 
469                 char[] ChunkRiff = { 'R', 'I', 'F', 'F' };
470                 char[] ChunkType = { 'W', 'A', 'V', 'E' };
471                 char[] ChunkFmt = { 'f', 'm', 't', ' ' };
472                 char[] ChunkData = { 'd', 'a', 't', 'a' };
473                 short shPad = 1;                // File padding 
474                 int nFormatChunkLength = 0x10;  // Format chunk length. 
475                 int nLength = 0;                // File length, minus first 8 bytes of RIFF description. This will be filled in later. 
476                 short shBytesPerSample = 0;     // Bytes per sample.
477                 // 一个样本点的字节数目 
478                 if (8 == WavFormat.BitsPerSample && 1 == WavFormat.Channels)
479                 {
480                     shBytesPerSample = 1;
481                 }
482 
483                 else if ((8 == WavFormat.BitsPerSample && 2 == WavFormat.Channels) || (16 == WavFormat.BitsPerSample && 1 == WavFormat.Channels))
484                 {
485                     shBytesPerSample = 2;
486                 }
487 
488                 else if (16 == WavFormat.BitsPerSample && 2 == WavFormat.Channels)
489                 {
490                     shBytesPerSample = 4;
491                 }
492 
493                 // RIFF 块 
494 
495                 bwWriter.Write(ChunkRiff);
496 
497                 bwWriter.Write(nLength);
498 
499                 bwWriter.Write(ChunkType);
500                 // WAVE块 
501 
502                 bwWriter.Write(ChunkFmt);
503 
504                 bwWriter.Write(nFormatChunkLength);
505 
506                 bwWriter.Write(shPad);
507 
508                 bwWriter.Write((short)WavFormat.Channels);
509 
510                 bwWriter.Write(WavFormat.SampleRate);
511 
512                 bwWriter.Write(WavFormat.AverageBytesPerSecond);
513 
514                 bwWriter.Write(shBytesPerSample);
515 
516                 bwWriter.Write((short)WavFormat.BitsPerSample);
517 
518                 // 数据块 
519 
520                 bwWriter.Write(ChunkData);
521 
522                 bwWriter.Write((int)0);   // The sample length will be written in later. 
523             }
524             catch (Exception ex)
525             {
526                 throw new Exception(ex.Message);
527             }
528         }
529 
530 
531 
532 
533     }
534 }

 

posted @ 2018-07-30 10:34  咖喱gg  阅读(835)  评论(0编辑  收藏  举报