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