1. 程式人生 > >【轉】視訊監控平臺-GB28181-語音對講功能

【轉】視訊監控平臺-GB28181-語音對講功能

視訊監控平臺-GB28181-2016語音對講功能

很多人在問我,語音對講是怎麼流程, 實現了怎麼去測試,語音對講是在2014版本提出來的,我這裡詳細寫一遍國標28181-2016語音對講的功能介紹。(如需交流可聯絡QQ:123011785)

提前先把GB28181檢測需要注意的事項說一下:

1、語音對講其實主要的是音訊流的輸入輸出, 輸入裝置型別是136,輸出裝置型別是137

2、目前檢測的時候,海康攝像機是定製的檢測版本有固定的137型別id,一般海康攝像機是沒有配置音訊輸出通道的,需要自己通過SDK模擬一個音訊輸出裝置或則讓海康提供 28181檢測的韌體版本。

3、檢測時候需要用膝上型電腦模擬一個136音訊輸入的裝置,一般和客戶端繫結一起(或則後臺伺服器配置相應的id和客戶端繫結一起)

4、音訊輸入源一般是採用筆記本採集的音訊訊號,音訊採集程式碼後面的部落格提供。

下面看一下語音對講的流程:

其中, 信令 1 、
2 、 3 、 4 為語音廣播通知、 語音廣播應答訊息流程; 信令 5 、 1 2 、 1 3 、 1 4 、 1 5 、 1 6 為 S I P 服務
器接收到客戶端的呼叫請求通過 B 2 B UA 代理方式建立語音流接收者與媒體伺服器之間的媒體流信令
過程, 信令 6~1 1 為 S I P 伺服器通過三方呼叫控制建立媒體伺服器與語音流傳送者之間的媒體流信令
過程, 信令 1 7~2 0 為 S I P 伺服器斷開語音流接收者與媒體伺服器之間的媒體流信令過程, 信令 2 1~2 4
6 4
G B / T2 8 1 8 1 — 2 0 1 6
為 S I P 伺服器斷開媒體伺服器與語音流傳送者之間的媒體流信令過程。
命令流程描述如下:
a ) 1 : S I P 伺服器向語音流接收者傳送語音廣播通知訊息, 訊息中通過 T o 頭域標明作為目的地址
的語音流接收者 I D , 訊息採用 M e s s a g e 方法攜帶。
b ) 2 : 語音流接收者收到語音廣播通知訊息後, 向 S I P 伺服器傳送 2 0 0OK 響應。
c ) 3 : 語音流接收者向 S I P 伺服器傳送語音廣播應答訊息, 訊息中通過 T o 頭域標明作為目的地
址的 S I P 伺服器 I D , 訊息採用 M e s s a g e 方法攜帶。
d ) 4 : S I P 伺服器收到語音廣播應答訊息後, 向語音流接收者傳送 2 0 0OK 響應。
e ) 5 : 語音流接收者向 S I P 伺服器傳送 I n v i t e 訊息, 訊息中通過 T o 頭域標明作為目的地址的語音
流傳送者 I D , 訊息頭域中攜帶 S u b j e c t 欄位, 表明請求的語音流傳送者 I D 、 傳送方媒體流序列
號、 語音流接收者 I D 、 接收方媒體流序列號等引數, S D P 訊息體中 s 欄位為“ P l a y ” 代表實時點
播, m 欄位中媒體引數標識為“
a u d i o ” 表示請求語音媒體流。
f ) 6 : S I P 伺服器收到 I n v i t e 請求後, 通過三方呼叫控制建立媒體伺服器和語音流傳送者之間的
媒體連線。向媒體伺服器傳送 I n v i t e 訊息, 此訊息不攜帶 S D P 訊息體。
g ) 7 : 媒體伺服器收到 S I P 伺服器的 I n v i t e 請求後, 回覆 2 0 0OK 響應, 攜帶 S D P 訊息體, 訊息體
中描述了媒體伺服器接收媒體流的 I P 、 埠、 媒體格式等內容。
h ) 8 : S I P 伺服器收到媒體伺服器返回的 2 0 0OK 響應後, 向語音流傳送者傳送 I n v i t e 請求, 訊息
中通過 T o 頭域標明作為目的地址的語音流傳送者 I D , 訊息頭域中攜帶 S u b j e c t 欄位, 表明請
求的語音流傳送者 I D 、 傳送方媒體流序列號、 語音流接收者 I D 、 接收方媒體流序列號等引數,
請求中攜帶訊息 7 中媒體伺服器回覆的 2 0 0OK 響應訊息體, s 欄位為“ P l a y ” 代表實時點播,
m 欄位中媒體引數標識為“ a u d i o ” 表示請求語音媒體流, 增加 y 欄位描述 S S R C 值, f 欄位描述
媒體引數。
i ) 9 : 語音流傳送者收到 S I P 伺服器的 I n v i t e 請求後, 回覆 2 0 0OK 響應, 攜帶 S D P 訊息體, 訊息
體中描述了媒體流傳送者傳送媒體流的 I P 、 埠、 媒體格式、 S S R C 欄位等內容, s 欄位為

P l a y ” 代表實時點播,
m 欄位中媒體引數標識為“ a u d i o ” 表示請求語音媒體流。
j ) 1 0 : S I P 伺服器收到語音流傳送者返回的 2 0 0OK 響應後, 向媒體伺服器傳送 A C K 請求, 請求
中攜帶訊息 9 中語音流傳送者回復的 2 0 0OK 響應訊息體, 完成與媒體伺服器的 I n v i t e 會話
建立過程。
k ) 1 1 : S I P 伺服器收到語音流傳送者返回的 2 0 0OK 響應後, 向語音流傳送者傳送 A C K 請求, 請
求中不攜帶訊息體, 完成與語音流傳送者的 I n v i t e 會話建立過程。
l ) 1 2 : 完成三方呼叫控制後, S I P 伺服器通過 B 2 B UA 代理方式建立語音流接收者和媒體伺服器
之間的媒體連線。在訊息 5 中增加 S S R C 值, 轉發給媒體伺服器。
m ) 1 3 : 媒體伺服器收到 I n v i t e 請求, 回覆 2 0 0OK 響應, 攜帶 S D P 訊息體, 訊息體中描述了媒體服
務器傳送媒體流的 I P 、 埠、 媒體格式、 S S R C 值等內容,
s 欄位為“ P l a y ” 代表實時點播, m 欄位
中媒體引數標識為“
a u d i o ” 表示請求語音媒體流。
n ) 1 4 : S I P 伺服器將訊息 1 3 轉發給語音流接收者。
o ) 1 5 : 語音流接收者收到 2 0 0OK 響應後, 回覆 A C K 訊息, 完成與 S I P 伺服器的 I n v i t e 會話建立
過程。
p ) 1 6 : S I P 伺服器將訊息 1 5 轉發給媒體伺服器, 完成與媒體伺服器的 I n v i t e 會話建立過程。
q ) 1 7 : S I P 伺服器向語音流接收者傳送 B Y E 訊息, 斷開訊息 5 、 1 4 、 1 5 建立的 I n v i t e 會話。
r ) 1 8 : 語音流接收者收到 B Y E 訊息後回覆 2 0 0OK 響應, 會話斷開。
s ) 1 9 : S I P 伺服器向媒體伺服器傳送 B Y E 訊息, 斷開訊息 1 2 、 1 3 、 1 6 建立的同媒體伺服器的
I n v i t e 會話。

 

上面是28181協議裡面規定的流程,直接照搬過來,不管怎麼實現語音對講也要根據流程走。

下面我把抓包詳情貼上下:

 
  1. MESSAGE sip:[email protected]:5060 SIP/2.0

  2. Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-1047e076-A8X5JYC1

  3. From: <sip:[email protected]:5060>;tag=CN2Ei3Vu

  4. To: <sip:[email protected]

    :5060>

  5. Call-ID: [email protected]

  6. CSeq: 55 MESSAGE

  7. Contact: <sip:192.168.1.93:5060>

  8. Content-Type: Application/MANSCDP+xml

  9. Max-Forwards: 70

  10. User-Agent: iVMS 1.0

  11. Content-Length: 173

  12.  
  13. <?xml version="1.0"?>

  14. <Notify>

  15. <CmdType>Broadcast</CmdType>

  16. <SN>20</SN>

  17. <SourceID>64000000001360000001</SourceID>

  18. <TargetID>34020000001370000001</TargetID>

  19. </Notify>

  20. SIP/2.0 200 OK

  21. To: <sip:[email protected]:5060>;tag=75600014_53173353_c376baa4-b5f9-4f2a-a739-653dc3299ae1

  22. Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-1047e076-A8X5JYC1;received=192.168.1.93

  23. CSeq: 55 MESSAGE

  24. Call-ID: [email protected]

  25. From: <sip:[email protected]:5060>;tag=CN2Ei3Vu

  26. Content-Length: 0

  27.  
  28. MESSAGE sip:[email protected] SIP/2.0

  29. Call-ID: [email protected]

  30. CSeq: 1 MESSAGE

  31. From: <sip:[email protected]>;tag=78679367_53173353_5e822bd3-744e-4d50-a7ae-3dcb31308ad5

  32. To: <sip:[email protected]>

  33. Max-Forwards: 70

  34. Content-Encoding: GB2312

  35. Content-Type: Application/MANSCDP+xml

  36. Route: <sip:[email protected]:5060;lr>

  37. Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK5e822bd3-744e-4d50-a7ae-3dcb31308ad5_53173353_28675579067886

  38. Content-Length: 147

  39.  
  40. <?xml version="1.0"?>

  41. <Response>

  42. <CmdType>Broadcast</CmdType>

  43. <SN>20</SN>

  44. <DeviceID>34020000001370000001</DeviceID>

  45. <Result>OK</Result>

  46. </Response>SIP/2.0 200 OK

  47. Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK5e822bd3-744e-4d50-a7ae-3dcb31308ad5_53173353_28675579067886

  48. From: <sip:[email protected]>;tag=78679367_53173353_5e822bd3-744e-4d50-a7ae-3dcb31308ad5

  49. To: <sip:[email protected]>;tag=nVa5oJ2n

  50. Call-ID: [email protected]

  51. CSeq: 1 MESSAGE

  52. Contact: <sip:[email protected]:5060>

  53. Content-Length: 0

  54.  
  55. INVITE sip:[email protected] SIP/2.0

  56. Call-ID: [email protected]

  57. CSeq: 1 INVITE

  58. From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab

  59. To: <sip:[email protected]>

  60. Max-Forwards: 70

  61. Contact: "34020000002000000001" <sip:192.168.1.81:5060>

  62. Subject: 64000000001360000001:0-4-0,34020000002000000001:1

  63. Content-Type: application/sdp

  64. Route: <sip:[email protected]:5060;lr>

  65. Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675585450209

  66. Content-Length: 171

  67.  
  68. v=0

  69. o=64010000002020000001 0 0 IN IP4 192.168.1.81

  70. s=Play

  71. c=IN IP4 192.168.1.81

  72. t=0 0

  73. m=audio 8000 RTP/AVP 96

  74. a=recvonly

  75. a=rtpmap:96 PS/90000

  76. y=0100000001

  77. f=v/////a/1/8/1

  78. SIP/2.0 100 Trying

  79. Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675585450209

  80. From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab

  81. To: <sip:[email protected]>

  82. Call-ID: [email protected]

  83. CSeq: 1 INVITE

  84. Content-Length: 0

  85.  
  86. SIP/2.0 200 OK

  87. Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675585450209

  88. Record-Route: <sip:[email protected]:5060;lr>

  89. From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab

  90. To: <sip:[email protected]>;tag=Wn1J54GK

  91. Call-ID: [email protected]

  92. CSeq: 1 INVITE

  93. Contact: <sip:[email protected]:5060>

  94. Content-Type: application/sdp

  95. Content-Length: 180

  96.  
  97. v=0

  98. o=64000000001360000001 0 0 IN IP4 192.168.1.93

  99. s=Play

  100. c=IN IP4 192.168.1.93

  101. t=0 0

  102. m=audio 20104 RTP/AVP 8

  103. a=sendonly

  104. a=rtpmap:8 PS/90000

  105. y=0100000001

  106. f=v/////a/1/8/1

  107. ACK sip:[email protected]:5060 SIP/2.0

  108. Call-ID: [email protected]

  109. CSeq: 1 ACK

  110. From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab

  111. To: <sip:[email protected]>;tag=Wn1J54GK

  112. Max-Forwards: 70

  113. Route: <sip:[email protected]:5060;lr>

  114. Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675725931057

  115. Content-Length: 0

流程抓包截圖:

 

 

 

音訊流採用的G711格式:

 

下面貼上音訊採集程式碼的片段:

 
  1. #pragma once

  2. #include <mmsystem.h>

  3. #include <dsound.h>

  4. #include <memory.h>

  5. #include <list>

  6. #include <process.h>

  7. #include "WaveHeader.h"

  8. #include <math.h>

  9. #include "SoundRecord.h"

  10. #include "g711.h"

  11.  
  12. class CDSRecord : public CSoundRecord

  13. {

  14. public:

  15. CDSRecord(ISoundNotify* pNotify);

  16. ~CDSRecord(void);

  17.  
  18. LPDIRECTSOUNDCAPTURE m_pDSCapture;

  19. LPDIRECTSOUNDCAPTUREBUFFER m_pDSBCapture;

  20. LPDIRECTSOUNDNOTIFY m_pDSNotify;

  21. HINSTANCE m_hInst;

  22. bool m_bRecording;

  23. WAVEFORMATEX m_wfxInput;

  24. DSBPOSITIONNOTIFY m_aPosNotify[4];

  25. HANDLE m_hNotificationEvent;

  26. HANDLE m_hStopThreadEvent;

  27. BOOL m_abInputFormatSupported[20];

  28. DWORD m_dwCaptureBufferSize;

  29. DWORD m_dwNextCaptureOffset;

  30. DWORD m_dwNotifySize;

  31. HANDLE m_hThread;

  32. WAVEFORMATEX WaveFormat;

  33. float bSampleReal[1024];

  34. float bSampleImg[1024];

  35. bool InitDS(void);

  36. bool SaveDataToFile(LPCTSTR m_pathname);

  37. bool ReadCaptureBuffer(void);

  38. bool StarRecord(void);

  39. bool StopRecord(void);

  40. std::list<DWORD> SizeList;

  41. std::list<void*> BufList;

  42. CWaveHeader WaveHeader;

  43. bool Sampling(void);

  44. void FFT(float xreal [], float ximag [], int n);

  45.  
  46. //通知介面

  47. ISoundNotify* m_pISoundNotify;

  48.  
  49. // 銷燬

  50. void Destroy(void);

  51. // 開始

  52. bool Start(void);

  53. // 結束

  54. bool Stop(void);

  55.  
  56. protected:

  57. void bitrp(float xreal [], float ximag [], int n);

  58. void IFFT (float xreal [], float ximag [], int n);

  59. };


 

 
  1. #include "StdAfx.h"

  2. #include "DSRecord.h"

  3.  
  4. UINT ReceiveDataThread(void* pParam);

  5.  
  6. CDSRecord::CDSRecord(ISoundNotify* pNotify)

  7. :m_pDSCapture(0)

  8. ,m_pDSBCapture(0)

  9. ,m_pDSNotify(0)

  10. ,m_bRecording(false)

  11. {

  12. ZeroMemory(&bSampleReal,1024);

  13. ZeroMemory(&bSampleImg,1024);

  14.  
  15. m_pISoundNotify = pNotify;

  16.  
  17. }

  18.  
  19. CDSRecord::~CDSRecord(void)

  20. {

  21. while(!BufList.empty())

  22. {

  23. free(BufList.front());

  24. BufList.pop_front();

  25. }

  26. }

  27.  
  28. bool CDSRecord::InitDS(void)

  29. {

  30. HRESULT hr;

  31. // 建立DSC物件

  32. if( FAILED( hr = DirectSoundCaptureCreate(NULL, &m_pDSCapture, NULL ) ) )

  33. {

  34. _WRITE_LOG( LOG_LEVEL_WARNING, "建立DSC物件失敗");

  35. return false;

  36. }

  37.  
  38. //初始化錄音格式----------------------------------------------

  39. WaveFormat.wFormatTag = WAVE_FORMAT_PCM;

  40. WaveFormat.nSamplesPerSec = 8000;//G.711取樣頻率

  41. WaveFormat.wBitsPerSample = 16; //16位

  42. WaveFormat.nChannels = 2;

  43. WaveFormat.nBlockAlign = WaveFormat.nChannels * ( WaveFormat.wBitsPerSample / 8 );

  44. WaveFormat.nAvgBytesPerSec = WaveFormat.nBlockAlign * WaveFormat.nSamplesPerSec;

  45.  
  46. //初始化buffer

  47. // Create the capture buffer

  48. m_dwCaptureBufferSize = WaveFormat.nAvgBytesPerSec / 20*8; //設定8秒的緩衝區;

  49. m_dwNextCaptureOffset=0;

  50.  
  51. DSCBUFFERDESC dscbd;

  52. ZeroMemory( &dscbd, sizeof(dscbd) );

  53. dscbd.dwSize = sizeof(dscbd);

  54. dscbd.dwBufferBytes = m_dwCaptureBufferSize;

  55. dscbd.lpwfxFormat = &WaveFormat; // Set the format during creatation

  56.  
  57. if( FAILED( hr = m_pDSCapture->CreateCaptureBuffer(&dscbd, &m_pDSBCapture, NULL ) ) )

  58. {

  59. _WRITE_LOG(LOG_LEVEL_WARNING,"建立緩衝區失敗");

  60. return false;

  61. }

  62.  
  63. //初始化訊息

  64. // Create a notification event, for when the sound stops playing

  65. if( FAILED( hr = m_pDSBCapture->QueryInterface( IID_IDirectSoundNotify,(VOID**)&m_pDSNotify ) ) )

  66. {

  67. _WRITE_LOG(LOG_LEVEL_WARNING,"建立訊息介面失敗");

  68. return false;

  69. }

  70.  
  71. // Set the notification size

  72. m_dwNotifySize =m_dwCaptureBufferSize/4; //到檔案尾時

  73. m_dwNotifySize -=m_dwNotifySize %WaveFormat.nBlockAlign;

  74.  
  75. // Setup the notification positions

  76. m_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

  77. m_hStopThreadEvent = CreateEvent(NULL,FALSE,FALSE, NULL);

  78.  
  79. ZeroMemory( &m_aPosNotify, sizeof(DSBPOSITIONNOTIFY) * 4 );

  80. for(int i=0;i<4;i++)

  81. {

  82. m_aPosNotify[i].dwOffset = m_dwNotifySize*i%m_dwCaptureBufferSize;

  83. m_aPosNotify[i].hEventNotify = m_hNotificationEvent;

  84. }

  85.  
  86. // Tell DirectSound when to notify us. the notification will come in the from

  87. // of signaled events that are handled in WinMain()

  88. if( FAILED( hr =m_pDSNotify->SetNotificationPositions(2, m_aPosNotify) ) )

  89. {

  90. _WRITE_LOG(LOG_LEVEL_WARNING,"設定訊息位置失敗");

  91. return false;

  92. }

  93. return true;

  94. }

  95.  
  96.  
  97.  
  98. bool CDSRecord::ReadCaptureBuffer(void)

  99. {

  100. if(!m_pDSBCapture|!m_bRecording)

  101. return false;

  102.  
  103. HRESULT hr;

  104. VOID* pbCaptureData = NULL;

  105. DWORD dwCaptureLength=0;

  106. VOID* pbCaptureData2 = NULL;

  107. DWORD dwCaptureLength2=0;

  108.  
  109. DWORD dwReadPos;

  110. DWORD dwCapturePos;

  111. LONG lLockSize;

  112.  
  113. if( FAILED( hr = m_pDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) )

  114. {

  115. this->m_bRecording=false;

  116. _WRITE_LOG(LOG_LEVEL_WARNING,"讀取當前指標位置錯誤");

  117. }

  118.  
  119. lLockSize = dwReadPos - m_dwNextCaptureOffset;

  120. if( lLockSize < 0 )

  121. lLockSize += m_dwCaptureBufferSize;

  122. if( lLockSize == 0 )

  123. return false;

  124.  
  125.  
  126.  
  127. // Lock the capture buffer down

  128. if( FAILED( hr = m_pDSBCapture->Lock( m_dwNextCaptureOffset, lLockSize,

  129. &pbCaptureData, &dwCaptureLength,

  130. &pbCaptureData2, &dwCaptureLength2, 0L ) ) )

  131. return false;

  132.  
  133. if(dwCaptureLength+dwCaptureLength2!=lLockSize)

  134. _WRITE_LOG(LOG_LEVEL_WARNING,"error");

  135.  
  136. // Write the data into list

  137. WaveHeader.dwRIFFLen+=lLockSize;

  138. WaveHeader.dwdataLen+=lLockSize;

  139.  
  140.  
  141. void* TmpBuf=malloc(lLockSize);

  142. //BufList.push_back(TmpBuf);

  143. //SizeList.push_back(lLockSize);

  144. memcpy(TmpBuf,pbCaptureData,dwCaptureLength);

  145.  
  146. if( pbCaptureData2)

  147. {

  148. memcpy((BYTE*)TmpBuf+dwCaptureLength,pbCaptureData2,dwCaptureLength2);

  149. }

  150.  
  151. int G711Size = 0;

  152. unsigned char* G711Buf = (unsigned char*)malloc(lLockSize/2);

  153.  
  154.  
  155. for(int i = 0;i < lLockSize/2;i++)

  156. {

  157. int out_size = 1;//8位G711

  158. unsigned char g711_val = 0;

  159.  
  160. g711_val = linear2alaw(*((short*)TmpBuf + i));

  161. memcpy(G711Buf + G711Size,&g711_val,out_size);

  162. G711Size += out_size;

  163. }

  164. #if 0

  165. static FILE* m_myfVideo = NULL;

  166. if (m_myfVideo == NULL)

  167. {

  168. m_myfVideo = fopen("c:\\audio.pcm", "ab+");

  169. }

  170. fwrite(G711Buf,1,G711Size,m_myfVideo);

  171. #endif

  172. m_pISoundNotify->OnGetAudioData(G711Buf,G711Size);

  173.  
  174. // Unlock the capture buffer

  175. m_pDSBCapture->Unlock( pbCaptureData, dwCaptureLength,

  176. pbCaptureData2, dwCaptureLength2 );

  177.  
  178. //讓m_dwNextCaptureOffset向前移動到讀指標處

  179. m_dwNextCaptureOffset+=lLockSize;

  180. m_dwNextCaptureOffset=m_dwNextCaptureOffset% m_dwCaptureBufferSize;

  181.  
  182. free(TmpBuf);

  183. free(G711Buf);

  184.  
  185. return true;

  186. }

  187.  
  188. bool CDSRecord::Sampling(void)

  189. {

  190. if(!m_pDSBCapture|!m_bRecording )

  191. return false;

  192.  
  193. HRESULT hr;

  194. VOID* pbCaptureData = NULL;

  195. DWORD dwCaptureLength=0;

  196. VOID* pbCaptureData2 = NULL;

  197. DWORD dwCaptureLength2=0;

  198.  
  199. DWORD dwReadPos;

  200. DWORD dwCapturePos;

  201. LONG lLockSize;

  202. DWORD dwStartPos;

  203.  
  204.  
  205.  
  206. if( FAILED( hr = m_pDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) )

  207. {

  208. this->m_bRecording=false;

  209. _WRITE_LOG(LOG_LEVEL_WARNING,"讀取當前指標位置錯誤");

  210.  
  211. }

  212.  
  213. lLockSize = 1024;

  214. dwStartPos=dwReadPos-lLockSize;

  215. if(dwStartPos<0)

  216. dwStartPos+=lLockSize;

  217.  
  218.  
  219. // Lock the capture buffer down

  220. if( FAILED( hr = m_pDSBCapture->Lock( dwStartPos, lLockSize,

  221. &pbCaptureData, &dwCaptureLength,

  222. &pbCaptureData2, &dwCaptureLength2, 0L ) ) )

  223. return false;

  224.  
  225. for(DWORD i=0;i<dwCaptureLength;i++)

  226. {

  227. float pPos=*((BYTE*)pbCaptureData+i);

  228. this->bSampleReal[i]=pPos/255;

  229. this->bSampleImg[i]=0;

  230. }

  231.  
  232. if( pbCaptureData2)

  233. {

  234. for(DWORD i=0;i<dwCaptureLength2;i++)

  235. {

  236. BYTE* pPos=(BYTE*)pbCaptureData2+i;

  237. this->bSampleReal[dwCaptureLength+i]=(float)*pPos/255;

  238. this->bSampleImg[i]=0;

  239. }

  240. }

  241.  
  242. // Unlock the capture buffer

  243. m_pDSBCapture->Unlock( pbCaptureData, dwCaptureLength,

  244. pbCaptureData2, dwCaptureLength2 );

  245.  
  246. return true;

  247. }

  248.  
  249.  
  250. UINT ReceiveDataThread(void* pParam)

  251. {

  252. CDSRecord *pApp=(CDSRecord*)pParam;

  253. HANDLE hArray[2]={pApp->m_hNotificationEvent,pApp->m_hStopThreadEvent};

  254.  
  255. DWORD EventResult;

  256. DWORD HandleNumber=sizeof(hArray)/sizeof(HANDLE);

  257. while(pApp->m_bRecording)

  258. {

  259.  
  260. EventResult = WaitForMultipleObjects(

  261. HandleNumber,

  262. hArray,

  263. FALSE,

  264. INFINITE);

  265.  
  266. if(WAIT_OBJECT_0+1 == EventResult)

  267. break;

  268.  
  269. if(WAIT_OBJECT_0 == EventResult)

  270. {

  271. pApp->ReadCaptureBuffer();

  272. ResetEvent(pApp->m_hNotificationEvent);

  273. }

  274. }

  275.  
  276. return 0;

  277. }

  278.  
  279. bool CDSRecord::StarRecord(void)

  280. {

  281. if(!InitDS())

  282. return false;

  283.  
  284. if( FAILED(m_pDSBCapture->Start( DSCBSTART_LOOPING ) ) )

  285. {

  286. _WRITE_LOG(LOG_LEVEL_WARNING,"開始錄音失敗");

  287. return false;

  288. }

  289.  
  290. unsigned int ThrdAddr;

  291. m_hThread = (HANDLE) _beginthreadex(NULL,

  292. 0,

  293. (unsigned int (__stdcall *)(void *))ReceiveDataThread,

  294. (void *)(this),//NULL,

  295. 0, &ThrdAddr);

  296. this->m_bRecording=true;

  297. return true;

  298. }

  299.  
  300. bool CDSRecord::StopRecord(void)

  301. {

  302. if(m_pDSBCapture == 0)

  303. {

  304. return false;

  305. }

  306.  
  307.  
  308. if( FAILED(m_pDSBCapture->Stop() ) )

  309. {

  310. _WRITE_LOG(LOG_LEVEL_WARNING,"停止錄音失敗");

  311. return false;

  312. }

  313. SetEvent(m_hStopThreadEvent);

  314. this->m_bRecording=false;

  315. return true;

  316. }

  317.  
  318. bool CDSRecord::SaveDataToFile(LPCTSTR m_pathname)

  319. {

  320.  
  321. //m_pDSBCapture->GetFormat(&WaveHeader.WaveFormat, sizeof(WAVEFORMATEX),NULL);

  322.  
  323. //if(m_pathname)

  324. //{

  325. // CFile file(m_pathname,CFile::modeWrite|CFile::modeCreate);

  326. // file.Write(&WaveHeader,46);

  327.  
  328.  
  329. // for(;!BufList.empty();)

  330. // {

  331. // void* pBuf=BufList.front();

  332. // file.Write(pBuf,SizeList.front());

  333. // BufList.pop_front();

  334. // SizeList.pop_front();

  335. // free(pBuf);

  336. // }

  337. // file.Flush();

  338. // file.Close();

  339. //}

  340. //else

  341. //{

  342. // _WRITE_LOG(LOG_LEVEL_WARNING,"請選擇儲存路徑");

  343. //}

  344. return true;

  345. }

  346.  
  347. inline void swap (float &a, float &b)

  348. {

  349. float t;

  350. t = a;

  351. a = b;

  352. b = t;

  353. }

  354.  
  355. void CDSRecord::bitrp(float xreal [], float ximag [], int n)

  356. {

  357. // 位反轉置換 Bit-reversal Permutation

  358. int i, j, a, b, p;

  359.  
  360. for (i = 1, p = 0; i < n; i *= 2)

  361. {

  362. p ++;

  363. }

  364. for (i = 0; i < n; i ++)

  365. {

  366. a = i;

  367. b = 0;

  368. for (j = 0; j < p; j ++)

  369. {

  370. b = (b << 1) + (a & 1); // b = b * 2 + a % 2;

  371. a >>= 1; // a = a / 2;

  372. }

  373. if ( b > i)

  374. {

  375. swap (xreal [i], xreal [b]);

  376. swap (ximag [i], ximag [b]);

  377. }

  378. }

  379. }

  380.  
  381. void CDSRecord::FFT(float xreal [], float ximag [], int n)

  382. {

  383. const int N = 1024;

  384. const float PI = 3.1416;

  385. // 快速傅立葉變換,將複數 x 變換後仍儲存在 x 中,xreal, ximag 分別是 x 的實部和虛部

  386. float* wreal=new float [n / 2];

  387. float* wimag=new float [n / 2];

  388. float treal, timag, ureal, uimag, arg;

  389. int m, k, j, t, index1, index2;

  390.  
  391. bitrp (xreal, ximag, n);

  392.  
  393. // 計算 1 的前 n / 2 個 n 次方根的共軛複數 W'j = wreal [j] + i * wimag [j] , j = 0, 1, , n / 2 - 1

  394. arg = - 2 * PI / n;

  395. treal = cos (arg);

  396. timag = sin (arg);

  397. wreal [0] = 1.0;

  398. wimag [0] = 0.0;

  399. for (j = 1; j < n / 2; j ++)

  400. {

  401. wreal [j] = wreal [j - 1] * treal - wimag [j - 1] * timag;

  402. wimag [j] = wreal [j - 1] * timag + wimag [j - 1] * treal;

  403. }

  404.  
  405. for (m = 2; m <= n; m *= 2)

  406. {

  407. for (k = 0; k < n; k += m)

  408. {

  409. for (j = 0; j < m / 2; j ++)

  410. {

  411. index1 = k + j;

  412. index2 = index1 + m / 2;

  413. t = n * j / m; // 旋轉因子 w 的實部在 wreal [] 中的下標為 t

  414. treal = wreal [t] * xreal [index2] - wimag [t] * ximag [index2];

  415. timag = wreal [t] * ximag [index2] + wimag [t] * xreal [index2];

  416. ureal = xreal [index1];

  417. uimag = ximag [index1];

  418. xreal [index1] = ureal + treal;

  419. ximag [index1] = uimag + timag;

  420. xreal [index2] = ureal - treal;

  421. ximag [index2] = uimag - timag;

  422. }

  423. }

  424. }

  425.  
  426. delete []wreal;

  427. delete []wimag;

  428. }

  429.  
  430. void CDSRecord::IFFT (float xreal [], float ximag [], int n)

  431. {

  432. const float PI = 3.1416;

  433. // 快速傅立葉逆變換

  434. float * wreal=new float [n / 2];

  435. float * wimag=new float [n / 2];

  436. float treal, timag, ureal, uimag, arg;

  437. int m, k, j, t, index1, index2;

  438.  
  439. bitrp (xreal, ximag, n);

  440.  
  441. // 計算 1 的前 n / 2 個 n 次方根 Wj = wreal [j] + i * wimag [j] , j = 0, 1, , n / 2 - 1

  442. arg = 2 * PI / n;

  443. treal = cos (arg);

  444. timag = sin (arg);

  445. wreal [0] = 1.0;

  446. wimag [0] = 0.0;

  447. for (j = 1; j < n / 2; j ++)

  448. {

  449. wreal [j] = wreal [j - 1] * treal - wimag [j - 1] * timag;

  450. wimag [j] = wreal [j - 1] * timag + wimag [j - 1] * treal;

  451. }

  452.  
  453. for (m = 2; m <= n; m *= 2)

  454. {

  455. for (k = 0; k < n; k += m)

  456. {

  457. for (j = 0; j < m / 2; j ++)

  458. {

  459. index1 = k + j;

  460. index2 = index1 + m / 2;

  461. t = n * j / m; // 旋轉因子 w 的實部在 wreal [] 中的下標為 t

  462. treal = wreal [t] * xreal [index2] - wimag [t] * ximag [index2];

  463. timag = wreal [t] * ximag [index2] + wimag [t] * xreal [index2];

  464. ureal = xreal [index1];

  465. uimag = ximag [index1];

  466. xreal [index1] = ureal + treal;

  467. ximag [index1] = uimag + timag;

  468. xreal [index2] = ureal - treal;

  469. ximag [index2] = uimag - timag;

  470. }

  471. }

  472. }

  473.  
  474. for (j=0; j < n; j ++)

  475. {

  476. xreal [j] /= n;

  477. ximag [j] /= n;

  478. }

  479.  
  480. delete []wreal;

  481. delete []wimag;

  482. }

  483.  
  484. bool CDSRecord::Start(void)

  485. {

  486. return StarRecord();

  487. }

  488.  
  489. bool CDSRecord::Stop(void)

  490. {

  491. return StopRecord();

  492. }

  493.  
  494. void CDSRecord::Destroy(void)

  495. {

  496. Stop();

  497.  
  498. delete this;

  499.  
  500. return;

  501. }