【轉】視訊監控平臺-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協議裡面規定的流程,直接照搬過來,不管怎麼實現語音對講也要根據流程走。
下面我把抓包詳情貼上下:
-
MESSAGE sip:[email protected]:5060 SIP/2.0
-
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-1047e076-A8X5JYC1
-
From: <sip:[email protected]:5060>;tag=CN2Ei3Vu
-
To: <sip:[email protected]
-
Call-ID: [email protected]
-
CSeq: 55 MESSAGE
-
Contact: <sip:192.168.1.93:5060>
-
Content-Type: Application/MANSCDP+xml
-
Max-Forwards: 70
-
User-Agent: iVMS 1.0
-
Content-Length: 173
-
<?xml version="1.0"?>
-
<Notify>
-
<CmdType>Broadcast</CmdType>
-
<SN>20</SN>
-
<SourceID>64000000001360000001</SourceID>
-
<TargetID>34020000001370000001</TargetID>
-
</Notify>
-
SIP/2.0 200 OK
-
To: <sip:[email protected]:5060>;tag=75600014_53173353_c376baa4-b5f9-4f2a-a739-653dc3299ae1
-
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-1047e076-A8X5JYC1;received=192.168.1.93
-
CSeq: 55 MESSAGE
-
Call-ID: [email protected]
-
From: <sip:[email protected]:5060>;tag=CN2Ei3Vu
-
Content-Length: 0
-
MESSAGE sip:[email protected] SIP/2.0
-
Call-ID: [email protected]
-
CSeq: 1 MESSAGE
-
From: <sip:[email protected]>;tag=78679367_53173353_5e822bd3-744e-4d50-a7ae-3dcb31308ad5
-
To: <sip:[email protected]>
-
Max-Forwards: 70
-
Content-Encoding: GB2312
-
Content-Type: Application/MANSCDP+xml
-
Route: <sip:[email protected]:5060;lr>
-
Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK5e822bd3-744e-4d50-a7ae-3dcb31308ad5_53173353_28675579067886
-
Content-Length: 147
-
<?xml version="1.0"?>
-
<Response>
-
<CmdType>Broadcast</CmdType>
-
<SN>20</SN>
-
<DeviceID>34020000001370000001</DeviceID>
-
<Result>OK</Result>
-
</Response>SIP/2.0 200 OK
-
Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK5e822bd3-744e-4d50-a7ae-3dcb31308ad5_53173353_28675579067886
-
From: <sip:[email protected]>;tag=78679367_53173353_5e822bd3-744e-4d50-a7ae-3dcb31308ad5
-
To: <sip:[email protected]>;tag=nVa5oJ2n
-
Call-ID: [email protected]
-
CSeq: 1 MESSAGE
-
Contact: <sip:[email protected]:5060>
-
Content-Length: 0
-
INVITE sip:[email protected] SIP/2.0
-
Call-ID: [email protected]
-
CSeq: 1 INVITE
-
From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab
-
To: <sip:[email protected]>
-
Max-Forwards: 70
-
Contact: "34020000002000000001" <sip:192.168.1.81:5060>
-
Subject: 64000000001360000001:0-4-0,34020000002000000001:1
-
Content-Type: application/sdp
-
Route: <sip:[email protected]:5060;lr>
-
Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675585450209
-
Content-Length: 171
-
v=0
-
o=64010000002020000001 0 0 IN IP4 192.168.1.81
-
s=Play
-
c=IN IP4 192.168.1.81
-
t=0 0
-
m=audio 8000 RTP/AVP 96
-
a=recvonly
-
a=rtpmap:96 PS/90000
-
y=0100000001
-
f=v/////a/1/8/1
-
SIP/2.0 100 Trying
-
Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675585450209
-
From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab
-
To: <sip:[email protected]>
-
Call-ID: [email protected]
-
CSeq: 1 INVITE
-
Content-Length: 0
-
SIP/2.0 200 OK
-
Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675585450209
-
Record-Route: <sip:[email protected]:5060;lr>
-
From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab
-
To: <sip:[email protected]>;tag=Wn1J54GK
-
Call-ID: [email protected]
-
CSeq: 1 INVITE
-
Contact: <sip:[email protected]:5060>
-
Content-Type: application/sdp
-
Content-Length: 180
-
v=0
-
o=64000000001360000001 0 0 IN IP4 192.168.1.93
-
s=Play
-
c=IN IP4 192.168.1.93
-
t=0 0
-
m=audio 20104 RTP/AVP 8
-
a=sendonly
-
a=rtpmap:8 PS/90000
-
y=0100000001
-
f=v/////a/1/8/1
-
ACK sip:[email protected]:5060 SIP/2.0
-
Call-ID: [email protected]
-
CSeq: 1 ACK
-
From: <sip:[email protected]>;tag=84133916_53173353_4063c926-989f-4a9a-af9c-867f8219c6ab
-
To: <sip:[email protected]>;tag=Wn1J54GK
-
Max-Forwards: 70
-
Route: <sip:[email protected]:5060;lr>
-
Via: SIP/2.0/UDP 192.168.1.81:5060;branch=z9hG4bK4063c926-989f-4a9a-af9c-867f8219c6ab_53173353_28675725931057
-
Content-Length: 0
流程抓包截圖:
音訊流採用的G711格式:
下面貼上音訊採集程式碼的片段:
-
#pragma once
-
#include <mmsystem.h>
-
#include <dsound.h>
-
#include <memory.h>
-
#include <list>
-
#include <process.h>
-
#include "WaveHeader.h"
-
#include <math.h>
-
#include "SoundRecord.h"
-
#include "g711.h"
-
class CDSRecord : public CSoundRecord
-
{
-
public:
-
CDSRecord(ISoundNotify* pNotify);
-
~CDSRecord(void);
-
LPDIRECTSOUNDCAPTURE m_pDSCapture;
-
LPDIRECTSOUNDCAPTUREBUFFER m_pDSBCapture;
-
LPDIRECTSOUNDNOTIFY m_pDSNotify;
-
HINSTANCE m_hInst;
-
bool m_bRecording;
-
WAVEFORMATEX m_wfxInput;
-
DSBPOSITIONNOTIFY m_aPosNotify[4];
-
HANDLE m_hNotificationEvent;
-
HANDLE m_hStopThreadEvent;
-
BOOL m_abInputFormatSupported[20];
-
DWORD m_dwCaptureBufferSize;
-
DWORD m_dwNextCaptureOffset;
-
DWORD m_dwNotifySize;
-
HANDLE m_hThread;
-
WAVEFORMATEX WaveFormat;
-
float bSampleReal[1024];
-
float bSampleImg[1024];
-
bool InitDS(void);
-
bool SaveDataToFile(LPCTSTR m_pathname);
-
bool ReadCaptureBuffer(void);
-
bool StarRecord(void);
-
bool StopRecord(void);
-
std::list<DWORD> SizeList;
-
std::list<void*> BufList;
-
CWaveHeader WaveHeader;
-
bool Sampling(void);
-
void FFT(float xreal [], float ximag [], int n);
-
//通知介面
-
ISoundNotify* m_pISoundNotify;
-
// 銷燬
-
void Destroy(void);
-
// 開始
-
bool Start(void);
-
// 結束
-
bool Stop(void);
-
protected:
-
void bitrp(float xreal [], float ximag [], int n);
-
void IFFT (float xreal [], float ximag [], int n);
-
};
-
#include "StdAfx.h"
-
#include "DSRecord.h"
-
UINT ReceiveDataThread(void* pParam);
-
CDSRecord::CDSRecord(ISoundNotify* pNotify)
-
:m_pDSCapture(0)
-
,m_pDSBCapture(0)
-
,m_pDSNotify(0)
-
,m_bRecording(false)
-
{
-
ZeroMemory(&bSampleReal,1024);
-
ZeroMemory(&bSampleImg,1024);
-
m_pISoundNotify = pNotify;
-
}
-
CDSRecord::~CDSRecord(void)
-
{
-
while(!BufList.empty())
-
{
-
free(BufList.front());
-
BufList.pop_front();
-
}
-
}
-
bool CDSRecord::InitDS(void)
-
{
-
HRESULT hr;
-
// 建立DSC物件
-
if( FAILED( hr = DirectSoundCaptureCreate(NULL, &m_pDSCapture, NULL ) ) )
-
{
-
_WRITE_LOG( LOG_LEVEL_WARNING, "建立DSC物件失敗");
-
return false;
-
}
-
//初始化錄音格式----------------------------------------------
-
WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
-
WaveFormat.nSamplesPerSec = 8000;//G.711取樣頻率
-
WaveFormat.wBitsPerSample = 16; //16位
-
WaveFormat.nChannels = 2;
-
WaveFormat.nBlockAlign = WaveFormat.nChannels * ( WaveFormat.wBitsPerSample / 8 );
-
WaveFormat.nAvgBytesPerSec = WaveFormat.nBlockAlign * WaveFormat.nSamplesPerSec;
-
//初始化buffer
-
// Create the capture buffer
-
m_dwCaptureBufferSize = WaveFormat.nAvgBytesPerSec / 20*8; //設定8秒的緩衝區;
-
m_dwNextCaptureOffset=0;
-
DSCBUFFERDESC dscbd;
-
ZeroMemory( &dscbd, sizeof(dscbd) );
-
dscbd.dwSize = sizeof(dscbd);
-
dscbd.dwBufferBytes = m_dwCaptureBufferSize;
-
dscbd.lpwfxFormat = &WaveFormat; // Set the format during creatation
-
if( FAILED( hr = m_pDSCapture->CreateCaptureBuffer(&dscbd, &m_pDSBCapture, NULL ) ) )
-
{
-
_WRITE_LOG(LOG_LEVEL_WARNING,"建立緩衝區失敗");
-
return false;
-
}
-
//初始化訊息
-
// Create a notification event, for when the sound stops playing
-
if( FAILED( hr = m_pDSBCapture->QueryInterface( IID_IDirectSoundNotify,(VOID**)&m_pDSNotify ) ) )
-
{
-
_WRITE_LOG(LOG_LEVEL_WARNING,"建立訊息介面失敗");
-
return false;
-
}
-
// Set the notification size
-
m_dwNotifySize =m_dwCaptureBufferSize/4; //到檔案尾時
-
m_dwNotifySize -=m_dwNotifySize %WaveFormat.nBlockAlign;
-
// Setup the notification positions
-
m_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
-
m_hStopThreadEvent = CreateEvent(NULL,FALSE,FALSE, NULL);
-
ZeroMemory( &m_aPosNotify, sizeof(DSBPOSITIONNOTIFY) * 4 );
-
for(int i=0;i<4;i++)
-
{
-
m_aPosNotify[i].dwOffset = m_dwNotifySize*i%m_dwCaptureBufferSize;
-
m_aPosNotify[i].hEventNotify = m_hNotificationEvent;
-
}
-
// Tell DirectSound when to notify us. the notification will come in the from
-
// of signaled events that are handled in WinMain()
-
if( FAILED( hr =m_pDSNotify->SetNotificationPositions(2, m_aPosNotify) ) )
-
{
-
_WRITE_LOG(LOG_LEVEL_WARNING,"設定訊息位置失敗");
-
return false;
-
}
-
return true;
-
}
-
bool CDSRecord::ReadCaptureBuffer(void)
-
{
-
if(!m_pDSBCapture|!m_bRecording)
-
return false;
-
HRESULT hr;
-
VOID* pbCaptureData = NULL;
-
DWORD dwCaptureLength=0;
-
VOID* pbCaptureData2 = NULL;
-
DWORD dwCaptureLength2=0;
-
DWORD dwReadPos;
-
DWORD dwCapturePos;
-
LONG lLockSize;
-
if( FAILED( hr = m_pDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) )
-
{
-
this->m_bRecording=false;
-
_WRITE_LOG(LOG_LEVEL_WARNING,"讀取當前指標位置錯誤");
-
}
-
lLockSize = dwReadPos - m_dwNextCaptureOffset;
-
if( lLockSize < 0 )
-
lLockSize += m_dwCaptureBufferSize;
-
if( lLockSize == 0 )
-
return false;
-
// Lock the capture buffer down
-
if( FAILED( hr = m_pDSBCapture->Lock( m_dwNextCaptureOffset, lLockSize,
-
&pbCaptureData, &dwCaptureLength,
-
&pbCaptureData2, &dwCaptureLength2, 0L ) ) )
-
return false;
-
if(dwCaptureLength+dwCaptureLength2!=lLockSize)
-
_WRITE_LOG(LOG_LEVEL_WARNING,"error");
-
// Write the data into list
-
WaveHeader.dwRIFFLen+=lLockSize;
-
WaveHeader.dwdataLen+=lLockSize;
-
void* TmpBuf=malloc(lLockSize);
-
//BufList.push_back(TmpBuf);
-
//SizeList.push_back(lLockSize);
-
memcpy(TmpBuf,pbCaptureData,dwCaptureLength);
-
if( pbCaptureData2)
-
{
-
memcpy((BYTE*)TmpBuf+dwCaptureLength,pbCaptureData2,dwCaptureLength2);
-
}
-
int G711Size = 0;
-
unsigned char* G711Buf = (unsigned char*)malloc(lLockSize/2);
-
for(int i = 0;i < lLockSize/2;i++)
-
{
-
int out_size = 1;//8位G711
-
unsigned char g711_val = 0;
-
g711_val = linear2alaw(*((short*)TmpBuf + i));
-
memcpy(G711Buf + G711Size,&g711_val,out_size);
-
G711Size += out_size;
-
}
-
#if 0
-
static FILE* m_myfVideo = NULL;
-
if (m_myfVideo == NULL)
-
{
-
m_myfVideo = fopen("c:\\audio.pcm", "ab+");
-
}
-
fwrite(G711Buf,1,G711Size,m_myfVideo);
-
#endif
-
m_pISoundNotify->OnGetAudioData(G711Buf,G711Size);
-
// Unlock the capture buffer
-
m_pDSBCapture->Unlock( pbCaptureData, dwCaptureLength,
-
pbCaptureData2, dwCaptureLength2 );
-
//讓m_dwNextCaptureOffset向前移動到讀指標處
-
m_dwNextCaptureOffset+=lLockSize;
-
m_dwNextCaptureOffset=m_dwNextCaptureOffset% m_dwCaptureBufferSize;
-
free(TmpBuf);
-
free(G711Buf);
-
return true;
-
}
-
bool CDSRecord::Sampling(void)
-
{
-
if(!m_pDSBCapture|!m_bRecording )
-
return false;
-
HRESULT hr;
-
VOID* pbCaptureData = NULL;
-
DWORD dwCaptureLength=0;
-
VOID* pbCaptureData2 = NULL;
-
DWORD dwCaptureLength2=0;
-
DWORD dwReadPos;
-
DWORD dwCapturePos;
-
LONG lLockSize;
-
DWORD dwStartPos;
-
if( FAILED( hr = m_pDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) )
-
{
-
this->m_bRecording=false;
-
_WRITE_LOG(LOG_LEVEL_WARNING,"讀取當前指標位置錯誤");
-
}
-
lLockSize = 1024;
-
dwStartPos=dwReadPos-lLockSize;
-
if(dwStartPos<0)
-
dwStartPos+=lLockSize;
-
// Lock the capture buffer down
-
if( FAILED( hr = m_pDSBCapture->Lock( dwStartPos, lLockSize,
-
&pbCaptureData, &dwCaptureLength,
-
&pbCaptureData2, &dwCaptureLength2, 0L ) ) )
-
return false;
-
for(DWORD i=0;i<dwCaptureLength;i++)
-
{
-
float pPos=*((BYTE*)pbCaptureData+i);
-
this->bSampleReal[i]=pPos/255;
-
this->bSampleImg[i]=0;
-
}
-
if( pbCaptureData2)
-
{
-
for(DWORD i=0;i<dwCaptureLength2;i++)
-
{
-
BYTE* pPos=(BYTE*)pbCaptureData2+i;
-
this->bSampleReal[dwCaptureLength+i]=(float)*pPos/255;
-
this->bSampleImg[i]=0;
-
}
-
}
-
// Unlock the capture buffer
-
m_pDSBCapture->Unlock( pbCaptureData, dwCaptureLength,
-
pbCaptureData2, dwCaptureLength2 );
-
return true;
-
}
-
UINT ReceiveDataThread(void* pParam)
-
{
-
CDSRecord *pApp=(CDSRecord*)pParam;
-
HANDLE hArray[2]={pApp->m_hNotificationEvent,pApp->m_hStopThreadEvent};
-
DWORD EventResult;
-
DWORD HandleNumber=sizeof(hArray)/sizeof(HANDLE);
-
while(pApp->m_bRecording)
-
{
-
EventResult = WaitForMultipleObjects(
-
HandleNumber,
-
hArray,
-
FALSE,
-
INFINITE);
-
if(WAIT_OBJECT_0+1 == EventResult)
-
break;
-
if(WAIT_OBJECT_0 == EventResult)
-
{
-
pApp->ReadCaptureBuffer();
-
ResetEvent(pApp->m_hNotificationEvent);
-
}
-
}
-
return 0;
-
}
-
bool CDSRecord::StarRecord(void)
-
{
-
if(!InitDS())
-
return false;
-
if( FAILED(m_pDSBCapture->Start( DSCBSTART_LOOPING ) ) )
-
{
-
_WRITE_LOG(LOG_LEVEL_WARNING,"開始錄音失敗");
-
return false;
-
}
-
unsigned int ThrdAddr;
-
m_hThread = (HANDLE) _beginthreadex(NULL,
-
0,
-
(unsigned int (__stdcall *)(void *))ReceiveDataThread,
-
(void *)(this),//NULL,
-
0, &ThrdAddr);
-
this->m_bRecording=true;
-
return true;
-
}
-
bool CDSRecord::StopRecord(void)
-
{
-
if(m_pDSBCapture == 0)
-
{
-
return false;
-
}
-
if( FAILED(m_pDSBCapture->Stop() ) )
-
{
-
_WRITE_LOG(LOG_LEVEL_WARNING,"停止錄音失敗");
-
return false;
-
}
-
SetEvent(m_hStopThreadEvent);
-
this->m_bRecording=false;
-
return true;
-
}
-
bool CDSRecord::SaveDataToFile(LPCTSTR m_pathname)
-
{
-
//m_pDSBCapture->GetFormat(&WaveHeader.WaveFormat, sizeof(WAVEFORMATEX),NULL);
-
//if(m_pathname)
-
//{
-
// CFile file(m_pathname,CFile::modeWrite|CFile::modeCreate);
-
// file.Write(&WaveHeader,46);
-
// for(;!BufList.empty();)
-
// {
-
// void* pBuf=BufList.front();
-
// file.Write(pBuf,SizeList.front());
-
// BufList.pop_front();
-
// SizeList.pop_front();
-
// free(pBuf);
-
// }
-
// file.Flush();
-
// file.Close();
-
//}
-
//else
-
//{
-
// _WRITE_LOG(LOG_LEVEL_WARNING,"請選擇儲存路徑");
-
//}
-
return true;
-
}
-
inline void swap (float &a, float &b)
-
{
-
float t;
-
t = a;
-
a = b;
-
b = t;
-
}
-
void CDSRecord::bitrp(float xreal [], float ximag [], int n)
-
{
-
// 位反轉置換 Bit-reversal Permutation
-
int i, j, a, b, p;
-
for (i = 1, p = 0; i < n; i *= 2)
-
{
-
p ++;
-
}
-
for (i = 0; i < n; i ++)
-
{
-
a = i;
-
b = 0;
-
for (j = 0; j < p; j ++)
-
{
-
b = (b << 1) + (a & 1); // b = b * 2 + a % 2;
-
a >>= 1; // a = a / 2;
-
}
-
if ( b > i)
-
{
-
swap (xreal [i], xreal [b]);
-
swap (ximag [i], ximag [b]);
-
}
-
}
-
}
-
void CDSRecord::FFT(float xreal [], float ximag [], int n)
-
{
-
const int N = 1024;
-
const float PI = 3.1416;
-
// 快速傅立葉變換,將複數 x 變換後仍儲存在 x 中,xreal, ximag 分別是 x 的實部和虛部
-
float* wreal=new float [n / 2];
-
float* wimag=new float [n / 2];
-
float treal, timag, ureal, uimag, arg;
-
int m, k, j, t, index1, index2;
-
bitrp (xreal, ximag, n);
-
// 計算 1 的前 n / 2 個 n 次方根的共軛複數 W'j = wreal [j] + i * wimag [j] , j = 0, 1, , n / 2 - 1
-
arg = - 2 * PI / n;
-
treal = cos (arg);
-
timag = sin (arg);
-
wreal [0] = 1.0;
-
wimag [0] = 0.0;
-
for (j = 1; j < n / 2; j ++)
-
{
-
wreal [j] = wreal [j - 1] * treal - wimag [j - 1] * timag;
-
wimag [j] = wreal [j - 1] * timag + wimag [j - 1] * treal;
-
}
-
for (m = 2; m <= n; m *= 2)
-
{
-
for (k = 0; k < n; k += m)
-
{
-
for (j = 0; j < m / 2; j ++)
-
{
-
index1 = k + j;
-
index2 = index1 + m / 2;
-
t = n * j / m; // 旋轉因子 w 的實部在 wreal [] 中的下標為 t
-
treal = wreal [t] * xreal [index2] - wimag [t] * ximag [index2];
-
timag = wreal [t] * ximag [index2] + wimag [t] * xreal [index2];
-
ureal = xreal [index1];
-
uimag = ximag [index1];
-
xreal [index1] = ureal + treal;
-
ximag [index1] = uimag + timag;
-
xreal [index2] = ureal - treal;
-
ximag [index2] = uimag - timag;
-
}
-
}
-
}
-
delete []wreal;
-
delete []wimag;
-
}
-
void CDSRecord::IFFT (float xreal [], float ximag [], int n)
-
{
-
const float PI = 3.1416;
-
// 快速傅立葉逆變換
-
float * wreal=new float [n / 2];
-
float * wimag=new float [n / 2];
-
float treal, timag, ureal, uimag, arg;
-
int m, k, j, t, index1, index2;
-
bitrp (xreal, ximag, n);
-
// 計算 1 的前 n / 2 個 n 次方根 Wj = wreal [j] + i * wimag [j] , j = 0, 1, , n / 2 - 1
-
arg = 2 * PI / n;
-
treal = cos (arg);
-
timag = sin (arg);
-
wreal [0] = 1.0;
-
wimag [0] = 0.0;
-
for (j = 1; j < n / 2; j ++)
-
{
-
wreal [j] = wreal [j - 1] * treal - wimag [j - 1] * timag;
-
wimag [j] = wreal [j - 1] * timag + wimag [j - 1] * treal;
-
}
-
for (m = 2; m <= n; m *= 2)
-
{
-
for (k = 0; k < n; k += m)
-
{
-
for (j = 0; j < m / 2; j ++)
-
{
-
index1 = k + j;
-
index2 = index1 + m / 2;
-
t = n * j / m; // 旋轉因子 w 的實部在 wreal [] 中的下標為 t
-
treal = wreal [t] * xreal [index2] - wimag [t] * ximag [index2];
-
timag = wreal [t] * ximag [index2] + wimag [t] * xreal [index2];
-
ureal = xreal [index1];
-
uimag = ximag [index1];
-
xreal [index1] = ureal + treal;
-
ximag [index1] = uimag + timag;
-
xreal [index2] = ureal - treal;
-
ximag [index2] = uimag - timag;
-
}
-
}
-
}
-
for (j=0; j < n; j ++)
-
{
-
xreal [j] /= n;
-
ximag [j] /= n;
-
}
-
delete []wreal;
-
delete []wimag;
-
}
-
bool CDSRecord::Start(void)
-
{
-
return StarRecord();
-
}
-
bool CDSRecord::Stop(void)
-
{
-
return StopRecord();
-
}
-
void CDSRecord::Destroy(void)
-
{
-
Stop();
-
delete this;
-
return;
-
}