zoukankan      html  css  js  c++  java
  • 迁移基于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 }
  • 相关阅读:
    一则自用iptables例子解释
    SVN配置钩子文件限制提交文件时必须填写更新日志
    Nginx反向代理配置配置实例
    mysql 物理数据存放
    VisualVM、JConsole
    Rabbitmq
    pdm画表间结构
    tomcat jvm 参数优化
    【转载】Java导入导出excel
    【转载】使用 Google Guava 美化你的 Java 代码
  • 原文地址:https://www.cnblogs.com/gxrsprite/p/9389265.html
Copyright © 2011-2022 走看看