遷移基於Microsoft.DirectX的AudioRecoder類到SharpDX上

最近遷移項目到x64上,要處理的東西仍是蠻多的,因此我要在說一次,不到萬不得已不要用COM組件,要用COM組件也得首先考慮不須要咱們關心平臺的作法,或者得有64位版本。php

好比Office的COM組件調用,excel能夠用NPOI你們都知道了,若是你沒用收費的aspose,那麼你要操做其餘office好比word,ppt等能夠用NetOffice組件,雖然一樣是調用COM,可是x86 x64均可以使用,並且任意完整本地Office版本便可(97~)html

 

回到正題,此次是把基於 Microsoft.DirectX,Microsoft.DirectX.DirectSound這些32bit類庫的AudioRecoder類遷移到SharpDX上來實現跨平臺ui

引用this

SharpDXspa

SharpDX.DirectSound線程

便可excel

 

  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 }
相關文章
相關標籤/搜索