最近遷移項目到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 }