├── README.md
├── base64.cpp
├── base64.h
├── conf
└── asr.conf.xml
├── dr_wav.h
├── easywsclient.cpp
├── easywsclient.hpp
├── md5.c
├── md5.h
├── mod
└── mod_vadasr.dll
├── mod_vadasr.2015.vcxproj
├── mod_vadasr.cpp
├── mod_vadasr使用说明.docx
├── opusvad.h
├── queue.h
├── tinycthread.c
├── tinycthread.h
├── url.cpp
├── url.h
├── xfasr.cpp
└── xfasr.h
/README.md:
--------------------------------------------------------------------------------
1 | # Freeswitch VAD ASR 模块
2 |
3 | 本代码是基于Freeswitch插件方式的实现语音VAD检测和ASR语音识别功能,语音识别使用的是科大讯飞的实时语音转写接口,需要自行申请appid和appkey。模块采用esl事件方式送出识别结果和录音文件,具体使用方法参考如下说明:
4 | 1. 配置文件asr.conf
5 | ```
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ```
15 | 2. App(dialplan示例)
16 |
17 | App名称: vad
18 | App参数: [ ]
19 | ACTION: start 启动 stop 停止
20 | VAD_VOICE_FRAMES:判定检测到多久的语音包认为是有人说话了,单位ms
21 | VAD_SILINCE_FRAMES:判定检测到多久的静音包认为是静音了,单位ms
22 | NS_LEVEL:降噪级别
23 | ```
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ```
34 | 3. Esl event说明
35 | 一共有三个自定义事件:
36 | #### #define VAD_EVENT_START "vad::start"
37 | 检测到语音,输出自定义头: Vad-Status:start
38 | #### #define VAD_EVENT_STOP "vad::stop"
39 | 检测到静音,输出自定义头: Vad-Status:stop
40 | Vad-RecordFile: 录音路径
41 | #### #define VAD_EVENT_ASR "vad::asr"
42 | 在检测到静音后,输出语音文本,自定义头:Asr-Text:文本内容,注意文本内容可能为空,表示未识别出。
43 | ## 代码仅供各位FreeSwitch爱好者参考学习,如有问题可加QQ:1869731(微信同号)沟通交流!!!
44 |
--------------------------------------------------------------------------------
/base64.cpp:
--------------------------------------------------------------------------------
1 |
2 |
3 | #include "base64.h"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | void base64_encode_block(char *inData, int inlen, char *outData, int *outlen)
11 | {
12 | if (NULL == inData)
13 | {
14 | return;
15 | }
16 |
17 | int blocksize;
18 | blocksize = inlen * 8 / 6 + 3;
19 | unsigned char* buffer = new unsigned char[blocksize];
20 | memset(buffer, 0, blocksize);
21 | *outlen = EVP_EncodeBlock(buffer, (const unsigned char*)inData, inlen);
22 | strcpy(outData, (char*)buffer);
23 |
24 | delete buffer;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/base64.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef base64_h
3 | #define base64_h
4 |
5 | #include
6 |
7 | void base64_encode_block(char *inData, int inlen, char *outData, int *outlen);
8 |
9 |
10 | #endif /* base64_h */
11 |
--------------------------------------------------------------------------------
/conf/asr.conf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/easywsclient.cpp:
--------------------------------------------------------------------------------
1 |
2 | #ifdef _WIN32
3 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
4 | #define _CRT_SECURE_NO_WARNINGS // _CRT_SECURE_NO_WARNINGS for sscanf errors in MSVC2013 Express
5 | #endif
6 | #ifndef WIN32_LEAN_AND_MEAN
7 | #define WIN32_LEAN_AND_MEAN
8 | #endif
9 | #include
10 | #include
11 | #include
12 | #pragma comment( lib, "ws2_32" )
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #ifndef _SSIZE_T_DEFINED
19 | typedef int ssize_t;
20 | #define _SSIZE_T_DEFINED
21 | #endif
22 | #ifndef _SOCKET_T_DEFINED
23 | typedef SOCKET socket_t;
24 | #define _SOCKET_T_DEFINED
25 | #endif
26 | #ifndef snprintf
27 | #define snprintf _snprintf_s
28 | #endif
29 | #if _MSC_VER >=1600
30 | // vs2010 or later
31 | #include
32 | #else
33 | typedef __int8 int8_t;
34 | typedef unsigned __int8 uint8_t;
35 | typedef __int32 int32_t;
36 | typedef unsigned __int32 uint32_t;
37 | typedef __int64 int64_t;
38 | typedef unsigned __int64 uint64_t;
39 | #endif
40 | #define socketerrno WSAGetLastError()
41 | #define SOCKET_EAGAIN_EINPROGRESS WSAEINPROGRESS
42 | #define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK
43 | #else
44 | #include
45 | #include
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #include
54 | #include
55 | #ifndef _SOCKET_T_DEFINED
56 | typedef int socket_t;
57 | #define _SOCKET_T_DEFINED
58 | #endif
59 | #ifndef INVALID_SOCKET
60 | #define INVALID_SOCKET (-1)
61 | #endif
62 | #ifndef SOCKET_ERROR
63 | #define SOCKET_ERROR (-1)
64 | #endif
65 | #define closesocket(s) ::close(s)
66 | #include
67 | #define socketerrno errno
68 | #define SOCKET_EAGAIN_EINPROGRESS EAGAIN
69 | #define SOCKET_EWOULDBLOCK EWOULDBLOCK
70 | #endif
71 |
72 | #include
73 | #include
74 |
75 | #include "easywsclient.hpp"
76 |
77 | using easywsclient::Callback_Imp;
78 | using easywsclient::BytesCallback_Imp;
79 |
80 | namespace { // private module-only namespace
81 |
82 | socket_t hostname_connect(const std::string& hostname, int port) {
83 | struct addrinfo hints;
84 | struct addrinfo *result;
85 | struct addrinfo *p;
86 | int ret;
87 | socket_t sockfd = INVALID_SOCKET;
88 | char sport[16];
89 | memset(&hints, 0, sizeof(hints));
90 | hints.ai_family = AF_UNSPEC;
91 | hints.ai_socktype = SOCK_STREAM;
92 | snprintf(sport, 16, "%d", port);
93 | if ((ret = getaddrinfo(hostname.c_str(), sport, &hints, &result)) != 0)
94 | {
95 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
96 | return 1;
97 | }
98 | for(p = result; p != NULL; p = p->ai_next)
99 | {
100 | sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
101 | if (sockfd == INVALID_SOCKET) { continue; }
102 | if (connect(sockfd, p->ai_addr, p->ai_addrlen) != SOCKET_ERROR) {
103 | break;
104 | }
105 | closesocket(sockfd);
106 | sockfd = INVALID_SOCKET;
107 | }
108 | freeaddrinfo(result);
109 | return sockfd;
110 | }
111 |
112 |
113 | class _DummyWebSocket : public easywsclient::WebSocket
114 | {
115 | public:
116 | void poll(int timeout) { }
117 | void send(const std::string& message) { }
118 | void sendBinary(const std::string& message) { }
119 | void sendBinary(const std::vector& message) { }
120 | void sendPing() { }
121 | void close() { }
122 | readyStateValues getReadyState() const { return CLOSED; }
123 | void _dispatch(Callback_Imp & callable) { }
124 | void _dispatchBinary(BytesCallback_Imp& callable) { }
125 | };
126 |
127 |
128 | class _RealWebSocket : public easywsclient::WebSocket
129 | {
130 | public:
131 | // http://tools.ietf.org/html/rfc6455#section-5.2 Base Framing Protocol
132 | //
133 | // 0 1 2 3
134 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
135 | // +-+-+-+-+-------+-+-------------+-------------------------------+
136 | // |F|R|R|R| opcode|M| Payload len | Extended payload length |
137 | // |I|S|S|S| (4) |A| (7) | (16/64) |
138 | // |N|V|V|V| |S| | (if payload len==126/127) |
139 | // | |1|2|3| |K| | |
140 | // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
141 | // | Extended payload length continued, if payload len == 127 |
142 | // + - - - - - - - - - - - - - - - +-------------------------------+
143 | // | |Masking-key, if MASK set to 1 |
144 | // +-------------------------------+-------------------------------+
145 | // | Masking-key (continued) | Payload Data |
146 | // +-------------------------------- - - - - - - - - - - - - - - - +
147 | // : Payload Data continued ... :
148 | // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
149 | // | Payload Data continued ... |
150 | // +---------------------------------------------------------------+
151 | struct wsheader_type {
152 | unsigned header_size;
153 | bool fin;
154 | bool mask;
155 | enum opcode_type {
156 | CONTINUATION = 0x0,
157 | TEXT_FRAME = 0x1,
158 | BINARY_FRAME = 0x2,
159 | CLOSE = 8,
160 | PING = 9,
161 | PONG = 0xa,
162 | } opcode;
163 | int N0;
164 | uint64_t N;
165 | uint8_t masking_key[4];
166 | };
167 |
168 | std::vector rxbuf;
169 | std::vector txbuf;
170 | std::vector receivedData;
171 |
172 | socket_t sockfd;
173 | readyStateValues readyState;
174 | bool useMask;
175 | HANDLE write_buffer_mutex;
176 | void *callback_arg;
177 |
178 | _RealWebSocket(socket_t sockfd, bool useMask, void *arg) : sockfd(sockfd), readyState(OPEN), useMask(useMask) {
179 | write_buffer_mutex = CreateMutex(NULL, FALSE, NULL);
180 | callback_arg = arg;
181 | }
182 |
183 | ~_RealWebSocket()
184 | {
185 | CloseHandle(write_buffer_mutex);
186 | }
187 |
188 | readyStateValues getReadyState() const {
189 | return readyState;
190 | }
191 |
192 | void poll(int timeout) { // timeout in milliseconds
193 | if (readyState == CLOSED) {
194 | if (timeout > 0) {
195 | timeval tv = { timeout/1000, (timeout%1000) * 1000 };
196 | select(0, NULL, NULL, NULL, &tv);
197 | }
198 | return;
199 | }
200 | if (timeout != 0) {
201 | fd_set rfds;
202 | fd_set wfds;
203 | timeval tv = { timeout/1000, (timeout%1000) * 1000 };
204 | FD_ZERO(&rfds);
205 | FD_ZERO(&wfds);
206 | FD_SET(sockfd, &rfds);
207 | if (txbuf.size()) { FD_SET(sockfd, &wfds); }
208 | select(sockfd + 1, &rfds, &wfds, 0, timeout > 0 ? &tv : 0);
209 | }
210 | while (true) {
211 | // FD_ISSET(0, &rfds) will be true
212 | int N = rxbuf.size();
213 | ssize_t ret;
214 | rxbuf.resize(N + 1500);
215 | ret = recv(sockfd, (char*)&rxbuf[0] + N, 1500, 0);
216 | if (false) { }
217 | else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) {
218 | rxbuf.resize(N);
219 | break;
220 | }
221 | else if (ret <= 0) {
222 | rxbuf.resize(N);
223 | closesocket(sockfd);
224 | readyState = CLOSED;
225 | fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr);
226 | break;
227 | }
228 | else {
229 | rxbuf.resize(N + ret);
230 | }
231 | }
232 | WaitForSingleObject(write_buffer_mutex, INFINITE);
233 | while (txbuf.size()) {
234 | int ret = ::send(sockfd, (char*)&txbuf[0], txbuf.size(), 0);
235 | if (false) { } // ??
236 | else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) {
237 | break;
238 | }
239 | else if (ret <= 0) {
240 | closesocket(sockfd);
241 | readyState = CLOSED;
242 | fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr);
243 | break;
244 | }
245 | else {
246 | txbuf.erase(txbuf.begin(), txbuf.begin() + ret);
247 | }
248 | }
249 | ReleaseMutex(write_buffer_mutex);
250 | if (!txbuf.size() && readyState == CLOSING) {
251 | closesocket(sockfd);
252 | readyState = CLOSED;
253 | }
254 | }
255 |
256 | // Callable must have signature: void(const std::string & message).
257 | // Should work with C functions, C++ functors, and C++11 std::function and
258 | // lambda:
259 | //template
260 | //void dispatch(Callable callable)
261 | virtual void _dispatch(Callback_Imp & callable) {
262 | struct CallbackAdapter : public BytesCallback_Imp
263 | // Adapt void(const std::string&) to void(const std::string&)
264 | {
265 | Callback_Imp& callable;
266 | CallbackAdapter(Callback_Imp& callable) : callable(callable) { }
267 | void operator()(const std::vector& message, void* ptr) {
268 | std::string stringMessage(message.begin(), message.end());
269 | callable(stringMessage, ptr);
270 | }
271 | };
272 | CallbackAdapter bytesCallback(callable);
273 | _dispatchBinary(bytesCallback);
274 | }
275 |
276 | virtual void _dispatchBinary(BytesCallback_Imp & callable) {
277 | // TODO: consider acquiring a lock on rxbuf...
278 | while (true) {
279 | wsheader_type ws;
280 | if (rxbuf.size() < 2) { return; /* Need at least 2 */ }
281 | const uint8_t * data = (uint8_t *) &rxbuf[0]; // peek, but don't consume
282 | ws.fin = (data[0] & 0x80) == 0x80;
283 | ws.opcode = (wsheader_type::opcode_type) (data[0] & 0x0f);
284 | ws.mask = (data[1] & 0x80) == 0x80;
285 | ws.N0 = (data[1] & 0x7f);
286 | ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0);
287 | if (rxbuf.size() < ws.header_size) { return; /* Need: ws.header_size - rxbuf.size() */ }
288 | int i = 0;
289 | if (ws.N0 < 126) {
290 | ws.N = ws.N0;
291 | i = 2;
292 | }
293 | else if (ws.N0 == 126) {
294 | ws.N = 0;
295 | ws.N |= ((uint64_t) data[2]) << 8;
296 | ws.N |= ((uint64_t) data[3]) << 0;
297 | i = 4;
298 | }
299 | else if (ws.N0 == 127) {
300 | ws.N = 0;
301 | ws.N |= ((uint64_t) data[2]) << 56;
302 | ws.N |= ((uint64_t) data[3]) << 48;
303 | ws.N |= ((uint64_t) data[4]) << 40;
304 | ws.N |= ((uint64_t) data[5]) << 32;
305 | ws.N |= ((uint64_t) data[6]) << 24;
306 | ws.N |= ((uint64_t) data[7]) << 16;
307 | ws.N |= ((uint64_t) data[8]) << 8;
308 | ws.N |= ((uint64_t) data[9]) << 0;
309 | i = 10;
310 | }
311 | if (ws.mask) {
312 | ws.masking_key[0] = ((uint8_t) data[i+0]) << 0;
313 | ws.masking_key[1] = ((uint8_t) data[i+1]) << 0;
314 | ws.masking_key[2] = ((uint8_t) data[i+2]) << 0;
315 | ws.masking_key[3] = ((uint8_t) data[i+3]) << 0;
316 | }
317 | else {
318 | ws.masking_key[0] = 0;
319 | ws.masking_key[1] = 0;
320 | ws.masking_key[2] = 0;
321 | ws.masking_key[3] = 0;
322 | }
323 | if (rxbuf.size() < ws.header_size+ws.N) { return; /* Need: ws.header_size+ws.N - rxbuf.size() */ }
324 |
325 | // We got a whole message, now do something with it:
326 | if (false) { }
327 | else if (
328 | ws.opcode == wsheader_type::TEXT_FRAME
329 | || ws.opcode == wsheader_type::BINARY_FRAME
330 | || ws.opcode == wsheader_type::CONTINUATION
331 | ) {
332 | if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } }
333 | receivedData.insert(receivedData.end(), rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N);// just feed
334 | if (ws.fin) {
335 | callable((const std::vector) receivedData, callback_arg);
336 | receivedData.erase(receivedData.begin(), receivedData.end());
337 | std::vector ().swap(receivedData);// free memory
338 | }
339 | }
340 | else if (ws.opcode == wsheader_type::PING) {
341 | if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } }
342 | std::string data(rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N);
343 | sendData(wsheader_type::PONG, data.size(), data.begin(), data.end());
344 | }
345 | else if (ws.opcode == wsheader_type::PONG) { }
346 | else if (ws.opcode == wsheader_type::CLOSE) { close(); }
347 | else { fprintf(stderr, "ERROR: Got unexpected WebSocket message.\n"); close(); }
348 |
349 | rxbuf.erase(rxbuf.begin(), rxbuf.begin() + ws.header_size+(size_t)ws.N);
350 | }
351 | }
352 |
353 | void sendPing() {
354 | std::string empty;
355 | sendData(wsheader_type::PING, empty.size(), empty.begin(), empty.end());
356 | }
357 |
358 | void send(const std::string& message) {
359 | sendData(wsheader_type::TEXT_FRAME, message.size(), message.begin(), message.end());
360 | }
361 |
362 | void sendBinary(const std::string& message) {
363 | sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end());
364 | }
365 |
366 | void sendBinary(const std::vector& message) {
367 | sendData(wsheader_type::TEXT_FRAME, message.size(), message.begin(), message.end());
368 | }
369 |
370 | template
371 | void sendData(wsheader_type::opcode_type type, uint64_t message_size, Iterator message_begin, Iterator message_end) {
372 | // TODO:
373 | // Masking key should (must) be derived from a high quality random
374 | // number generator, to mitigate attacks on non-WebSocket friendly
375 | // middleware:
376 | const uint8_t masking_key[4] = { 0x12, 0x34, 0x56, 0x78 };
377 | // TODO: consider acquiring a lock on txbuf...
378 | if (readyState == CLOSING || readyState == CLOSED) { return; }
379 | std::vector header;
380 | header.assign(2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0) + (useMask ? 4 : 0), 0);
381 | header[0] = 0x80 | type;
382 | if (false) { }
383 | else if (message_size < 126) {
384 | header[1] = (message_size & 0xff) | (useMask ? 0x80 : 0);
385 | if (useMask) {
386 | header[2] = masking_key[0];
387 | header[3] = masking_key[1];
388 | header[4] = masking_key[2];
389 | header[5] = masking_key[3];
390 | }
391 | }
392 | else if (message_size < 65536) {
393 | header[1] = 126 | (useMask ? 0x80 : 0);
394 | header[2] = (message_size >> 8) & 0xff;
395 | header[3] = (message_size >> 0) & 0xff;
396 | if (useMask) {
397 | header[4] = masking_key[0];
398 | header[5] = masking_key[1];
399 | header[6] = masking_key[2];
400 | header[7] = masking_key[3];
401 | }
402 | }
403 | else { // TODO: run coverage testing here
404 | header[1] = 127 | (useMask ? 0x80 : 0);
405 | header[2] = (message_size >> 56) & 0xff;
406 | header[3] = (message_size >> 48) & 0xff;
407 | header[4] = (message_size >> 40) & 0xff;
408 | header[5] = (message_size >> 32) & 0xff;
409 | header[6] = (message_size >> 24) & 0xff;
410 | header[7] = (message_size >> 16) & 0xff;
411 | header[8] = (message_size >> 8) & 0xff;
412 | header[9] = (message_size >> 0) & 0xff;
413 | if (useMask) {
414 | header[10] = masking_key[0];
415 | header[11] = masking_key[1];
416 | header[12] = masking_key[2];
417 | header[13] = masking_key[3];
418 | }
419 | }
420 | WaitForSingleObject(write_buffer_mutex, INFINITE);
421 | // N.B. - txbuf will keep growing until it can be transmitted over the socket:
422 | txbuf.insert(txbuf.end(), header.begin(), header.end());
423 | txbuf.insert(txbuf.end(), message_begin, message_end);
424 | if (useMask) {
425 | for (size_t i = 0; i != message_size; ++i) { *(txbuf.end() - message_size + i) ^= masking_key[i&0x3]; }
426 | }
427 | ReleaseMutex(write_buffer_mutex);
428 | }
429 |
430 | void close() {
431 | if(readyState == CLOSING || readyState == CLOSED) { return; }
432 | readyState = CLOSING;
433 | uint8_t closeFrame[6] = {0x88, 0x80, 0x00, 0x00, 0x00, 0x00}; // last 4 bytes are a masking key
434 | std::vector header(closeFrame, closeFrame+6);
435 | WaitForSingleObject(write_buffer_mutex, INFINITE);
436 | txbuf.insert(txbuf.end(), header.begin(), header.end());
437 | ReleaseMutex(write_buffer_mutex);
438 | }
439 |
440 | };
441 |
442 |
443 | easywsclient::WebSocket::pointer from_url(const std::string& url, bool useMask, const std::string& origin, void *arg) {
444 | char host[128];
445 | int port;
446 | char path[128];
447 | if (url.size() >= 128) {
448 | fprintf(stderr, "ERROR: url size limit exceeded: %s\n", url.c_str());
449 | return NULL;
450 | }
451 | if (origin.size() >= 200) {
452 | fprintf(stderr, "ERROR: origin size limit exceeded: %s\n", origin.c_str());
453 | return NULL;
454 | }
455 | if (false) { }
456 | else if (sscanf(url.c_str(), "ws://%[^:/]:%d/%s", host, &port, path) == 3) {
457 | }
458 | else if (sscanf(url.c_str(), "ws://%[^:/]/%s", host, path) == 2) {
459 | port = 80;
460 | }
461 | else if (sscanf(url.c_str(), "ws://%[^:/]:%d", host, &port) == 2) {
462 | path[0] = '\0';
463 | }
464 | else if (sscanf(url.c_str(), "ws://%[^:/]", host) == 1) {
465 | port = 80;
466 | path[0] = '\0';
467 | }
468 | else {
469 | fprintf(stderr, "ERROR: Could not parse WebSocket url: %s\n", url.c_str());
470 | return NULL;
471 | }
472 | //fprintf(stderr, "easywsclient: connecting: host=%s port=%d path=/%s\n", host, port, path);
473 | socket_t sockfd = hostname_connect(host, port);
474 | if (sockfd == INVALID_SOCKET) {
475 | fprintf(stderr, "Unable to connect to %s:%d\n", host, port);
476 | return NULL;
477 | }
478 | {
479 | // XXX: this should be done non-blocking,
480 | char line[256];
481 | int status;
482 | int i;
483 | snprintf(line, 256, "GET /%s HTTP/1.1\r\n", path); ::send(sockfd, line, strlen(line), 0);
484 | if (port == 80) {
485 | snprintf(line, 256, "Host: %s\r\n", host); ::send(sockfd, line, strlen(line), 0);
486 | }
487 | else {
488 | snprintf(line, 256, "Host: %s:%d\r\n", host, port); ::send(sockfd, line, strlen(line), 0);
489 | }
490 | snprintf(line, 256, "Upgrade: websocket\r\n"); ::send(sockfd, line, strlen(line), 0);
491 | snprintf(line, 256, "Connection: Upgrade\r\n"); ::send(sockfd, line, strlen(line), 0);
492 | if (!origin.empty()) {
493 | snprintf(line, 256, "Origin: %s\r\n", origin.c_str()); ::send(sockfd, line, strlen(line), 0);
494 | }
495 | snprintf(line, 256, "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"); ::send(sockfd, line, strlen(line), 0);
496 | snprintf(line, 256, "Sec-WebSocket-Version: 13\r\n"); ::send(sockfd, line, strlen(line), 0);
497 | snprintf(line, 256, "\r\n"); ::send(sockfd, line, strlen(line), 0);
498 | for (i = 0; i < 2 || (i < 255 && line[i-2] != '\r' && line[i-1] != '\n'); ++i) { if (recv(sockfd, line+i, 1, 0) == 0) { return NULL; } }
499 | line[i] = 0;
500 | if (i == 255) { fprintf(stderr, "ERROR: Got invalid status line connecting to: %s\n", url.c_str()); return NULL; }
501 | if (sscanf(line, "HTTP/1.1 %d", &status) != 1 || status != 101) { fprintf(stderr, "ERROR: Got bad status connecting to %s: %s", url.c_str(), line); return NULL; }
502 | // TODO: verify response headers,
503 | while (true) {
504 | for (i = 0; i < 2 || (i < 255 && line[i-2] != '\r' && line[i-1] != '\n'); ++i) { if (recv(sockfd, line+i, 1, 0) == 0) { return NULL; } }
505 | if (line[0] == '\r' && line[1] == '\n') { break; }
506 | }
507 | }
508 | int flag = 1;
509 | setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm
510 | #ifdef _WIN32
511 | u_long on = 1;
512 | ioctlsocket(sockfd, FIONBIO, &on);
513 | #else
514 | fcntl(sockfd, F_SETFL, O_NONBLOCK);
515 | #endif
516 | //fprintf(stderr, "Connected to: %s\n", url.c_str());
517 | return easywsclient::WebSocket::pointer(new _RealWebSocket(sockfd, useMask, arg));
518 | }
519 |
520 | } // end of module-only namespace
521 |
522 |
523 |
524 | namespace easywsclient {
525 |
526 | WebSocket::pointer WebSocket::create_dummy() {
527 | static pointer dummy = pointer(new _DummyWebSocket);
528 | return dummy;
529 | }
530 |
531 |
532 | WebSocket::pointer WebSocket::from_url(const std::string& url, const std::string& origin, void *arg) {
533 | return ::from_url(url, true, origin, arg);
534 | }
535 |
536 | WebSocket::pointer WebSocket::from_url_no_mask(const std::string& url, const std::string& origin, void *arg) {
537 | return ::from_url(url, false, origin, arg);
538 | }
539 |
540 |
541 | } // namespace easywsclient
542 |
--------------------------------------------------------------------------------
/easywsclient.hpp:
--------------------------------------------------------------------------------
1 | #ifndef EASYWSCLIENT_HPP_20170819_MIOFVASDTNUASZDQPLFD
2 | #define EASYWSCLIENT_HPP_20170819_MIOFVASDTNUASZDQPLFD
3 |
4 | // This code comes from:
5 | // https://github.com/dhbaird/easywsclient
6 | //
7 | // To get the latest version:
8 | // wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.hpp
9 | // wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.cpp
10 |
11 | #include
12 | #include
13 |
14 | namespace easywsclient {
15 |
16 | struct Callback_Imp { virtual void operator()(const std::string& message, void* arg) = 0; };
17 | struct BytesCallback_Imp { virtual void operator()(const std::vector& message, void* arg) = 0; };
18 |
19 | class WebSocket {
20 | public:
21 | typedef WebSocket * pointer;
22 | typedef enum readyStateValues { CLOSING, CLOSED, CONNECTING, OPEN } readyStateValues;
23 |
24 | // Factories:
25 | static pointer create_dummy();
26 | static pointer from_url(const std::string& url, const std::string& origin = std::string(), void *arg = nullptr);
27 | static pointer from_url_no_mask(const std::string& url, const std::string& origin = std::string(), void *arg = nullptr);
28 |
29 | // Interfaces:
30 | virtual ~WebSocket() { }
31 | virtual void poll(int timeout = 0) = 0; // timeout in milliseconds
32 | virtual void send(const std::string& message) = 0;
33 | virtual void sendBinary(const std::string& message) = 0;
34 | virtual void sendBinary(const std::vector& message) = 0;
35 | virtual void sendPing() = 0;
36 | virtual void close() = 0;
37 | virtual readyStateValues getReadyState() const = 0;
38 |
39 | template
40 | void dispatch(Callable callable)
41 | // For callbacks that accept a string argument.
42 | { // N.B. this is compatible with both C++11 lambdas, functors and C function pointers
43 | struct _Callback : public Callback_Imp {
44 | Callable& callable;
45 | _Callback(Callable& callable) : callable(callable) { }
46 | void operator()(const std::string& message, void* arg) { callable(message,arg); }
47 | };
48 | _Callback callback(callable);
49 | _dispatch(callback);
50 | }
51 |
52 | template
53 | void dispatchBinary(Callable callable)
54 | // For callbacks that accept a std::vector argument.
55 | { // N.B. this is compatible with both C++11 lambdas, functors and C function pointers
56 | struct _Callback : public BytesCallback_Imp {
57 | Callable& callable;
58 | _Callback(Callable& callable) : callable(callable) { }
59 | void operator()(const std::vector& message, void* arg) { callable(message, arg); }
60 | };
61 | _Callback callback(callable);
62 | _dispatchBinary(callback);
63 | }
64 |
65 | protected:
66 | virtual void _dispatch(Callback_Imp& callable) = 0;
67 | virtual void _dispatchBinary(BytesCallback_Imp& callable) = 0;
68 | };
69 |
70 | } // namespace easywsclient
71 |
72 | #endif /* EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD */
73 |
--------------------------------------------------------------------------------
/md5.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "md5.h"
4 |
5 | static unsigned char PADDING[] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
9 | };
10 |
11 | static void MD5Init(MD5_CTX *context)
12 | {
13 | context->count[0] = 0;
14 | context->count[1] = 0;
15 | context->state[0] = 0x67452301;
16 | context->state[1] = 0xEFCDAB89;
17 | context->state[2] = 0x98BADCFE;
18 | context->state[3] = 0x10325476;
19 | }
20 |
21 | static void MD5Encode(unsigned char *output, unsigned int *input, unsigned int len)
22 | {
23 | unsigned int i = 0, j = 0;
24 | while (j < len)
25 | {
26 | output[j] = input[i] & 0xFF;
27 | output[j + 1] = (input[i] >> 8) & 0xFF;
28 | output[j + 2] = (input[i] >> 16) & 0xFF;
29 | output[j + 3] = (input[i] >> 24) & 0xFF;
30 | i++;
31 | j += 4;
32 | }
33 | }
34 |
35 | static void MD5Decode(unsigned int *output, unsigned char *input, unsigned int len)
36 | {
37 | unsigned int i = 0, j = 0;
38 | while (j < len)
39 | {
40 | output[i] = (input[j]) |
41 | (input[j + 1] << 8) |
42 | (input[j + 2] << 16) |
43 | (input[j + 3] << 24);
44 | i++;
45 | j += 4;
46 | }
47 | }
48 |
49 | static void MD5Transform(unsigned int state[4], unsigned char block[64])
50 | {
51 | unsigned int a = state[0];
52 | unsigned int b = state[1];
53 | unsigned int c = state[2];
54 | unsigned int d = state[3];
55 | unsigned int x[64];
56 | MD5Decode(x, block, 64);
57 | FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */
58 | FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */
59 | FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */
60 | FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
61 | FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */
62 | FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */
63 | FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */
64 | FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */
65 | FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */
66 | FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */
67 | FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
68 | FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
69 | FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
70 | FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
71 | FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
72 | FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
73 |
74 | /* Round 2 */
75 | GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
76 | GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
77 | GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
78 | GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
79 | GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */
80 | GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
81 | GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
82 | GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */
83 | GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */
84 | GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
85 | GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */
86 | GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */
87 | GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
88 | GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */
89 | GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */
90 | GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
91 |
92 | /* Round 3 */
93 | HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */
94 | HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */
95 | HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
96 | HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
97 | HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */
98 | HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */
99 | HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */
100 | HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
101 | HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
102 | HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */
103 | HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */
104 | HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */
105 | HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */
106 | HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
107 | HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
108 | HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */
109 |
110 | /* Round 4 */
111 | II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */
112 | II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */
113 | II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
114 | II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */
115 | II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
116 | II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */
117 | II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
118 | II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */
119 | II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */
120 | II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
121 | II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */
122 | II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
123 | II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */
124 | II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
125 | II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */
126 | II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */
127 | state[0] += a;
128 | state[1] += b;
129 | state[2] += c;
130 | state[3] += d;
131 | }
132 |
133 |
134 | static void MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen)
135 | {
136 | unsigned int i = 0, index = 0, partlen = 0;
137 | index = (context->count[0] >> 3) & 0x3F;
138 | partlen = 64 - index;
139 | context->count[0] += inputlen << 3;
140 | if (context->count[0] < (inputlen << 3))
141 | context->count[1]++;
142 | context->count[1] += inputlen >> 29;
143 |
144 | if (inputlen >= partlen)
145 | {
146 | memcpy(&context->buffer[index], input, partlen);
147 | MD5Transform(context->state, context->buffer);
148 | for (i = partlen; i + 64 <= inputlen; i += 64)
149 | MD5Transform(context->state, &input[i]);
150 | index = 0;
151 | }
152 | else
153 | {
154 | i = 0;
155 | }
156 | memcpy(&context->buffer[index], &input[i], inputlen - i);
157 | }
158 |
159 | static void MD5Final(MD5_CTX *context, unsigned char digest[16])
160 | {
161 | unsigned int index = 0, padlen = 0;
162 | unsigned char bits[8];
163 | index = (context->count[0] >> 3) & 0x3F;
164 | padlen = (index < 56) ? (56 - index) : (120 - index);
165 | MD5Encode(bits, context->count, 8);
166 | MD5Update(context, PADDING, padlen);
167 | MD5Update(context, bits, 8);
168 | MD5Encode(digest, context->state, 16);
169 | }
170 |
171 | extern void md5(unsigned char *data, char hex[36])
172 | {
173 | int i;
174 | unsigned char decrypt[16];
175 | MD5_CTX md5;
176 | MD5Init(&md5);
177 | MD5Update(&md5, data, strlen((char *)data));
178 | MD5Final(&md5, decrypt);
179 | for (i = 0; i < 16; i++)
180 | {
181 | snprintf(hex + i * 2, 3, "%02x", decrypt[i]);
182 | }
183 | hex[32] = 0;
184 | }
185 |
186 |
--------------------------------------------------------------------------------
/md5.h:
--------------------------------------------------------------------------------
1 | #ifndef MD5_H
2 | #define MD5_H
3 |
4 |
5 | typedef struct
6 | {
7 | unsigned int count[2];
8 | unsigned int state[4];
9 | unsigned char buffer[64];
10 | } MD5_CTX;
11 |
12 | #ifdef __cplusplus
13 | extern "C" {
14 | #endif
15 | void md5(unsigned char *data, char hex[36]);
16 | #ifdef __cplusplus
17 | };
18 | #endif
19 |
20 | #define F(x,y,z) ((x & y) | (~x & z))
21 | #define G(x,y,z) ((x & z) | (y & ~z))
22 | #define H(x,y,z) (x^y^z)
23 | #define I(x,y,z) (y ^ (x | ~z))
24 | #define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
25 | #define FF(a,b,c,d,x,s,ac) \
26 | { \
27 | a += F(b,c,d) + x + ac; \
28 | a = ROTATE_LEFT(a,s); \
29 | a += b; \
30 | }
31 | #define GG(a,b,c,d,x,s,ac) \
32 | { \
33 | a += G(b,c,d) + x + ac; \
34 | a = ROTATE_LEFT(a,s); \
35 | a += b; \
36 | }
37 | #define HH(a,b,c,d,x,s,ac) \
38 | { \
39 | a += H(b,c,d) + x + ac; \
40 | a = ROTATE_LEFT(a,s); \
41 | a += b; \
42 | }
43 | #define II(a,b,c,d,x,s,ac) \
44 | { \
45 | a += I(b,c,d) + x + ac; \
46 | a = ROTATE_LEFT(a,s); \
47 | a += b; \
48 | }
49 |
50 |
51 |
52 | #endif
53 |
54 |
55 |
--------------------------------------------------------------------------------
/mod/mod_vadasr.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanghaimoon888/mod_vadasr/900752f40bcd8e182e6967566c061547a1be31a4/mod/mod_vadasr.dll
--------------------------------------------------------------------------------
/mod_vadasr.2015.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | mod_vadasr
23 | {A175083A-4CE3-4A8D-A70F-A946DD28C364}
24 | mod_vad
25 | Win32Proj
26 |
27 |
28 |
29 | DynamicLibrary
30 | MultiByte
31 | v140
32 |
33 |
34 | DynamicLibrary
35 | MultiByte
36 | v140
37 |
38 |
39 | DynamicLibrary
40 | MultiByte
41 | v140
42 |
43 |
44 | DynamicLibrary
45 | MultiByte
46 | v140
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | <_ProjectFileVersion>10.0.30319.1
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | false
78 |
79 |
80 |
81 |
82 |
83 |
84 | X64
85 |
86 |
87 |
88 |
89 |
90 |
91 | false
92 |
93 |
94 | MachineX64
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | false
104 |
105 |
106 |
107 |
108 |
109 |
110 | X64
111 |
112 |
113 |
114 |
115 | _XKEYCHECK_H;%(PreprocessorDefinitions)
116 | D:\Project\Others\Project_C\AsrTest\OpenSSL\include;%(AdditionalIncludeDirectories)
117 |
118 |
119 | false
120 |
121 |
122 | MachineX64
123 | D:\Project\FWsrc\freeswitch-1.6.20-AI\x64\Release
124 | libeay32.lib;ssleay32.lib;%(AdditionalDependencies)
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | {202d7a4e-760d-4d0e-afa1-d7459ced30ff}
139 | false
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/mod_vadasr.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 | * Copyright (C) 2005/2012, Anthony Minessale II
4 | *
5 | * Version: MPL 1.1
6 | *
7 | * The contents of this file are subject to the Mozilla Public License Version
8 | * 1.1 (the "License"); you may not use this file except in compliance with
9 | * the License. You may obtain a copy of the License at
10 | * http://www.mozilla.org/MPL/
11 | *
12 | * Software distributed under the License is distributed on an "AS IS" basis,
13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 | * for the specific language governing rights and limitations under the
15 | * License.
16 | *
17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 | *
19 | * The Initial Developer of the Original Code is
20 | * Anthony Minessale II
21 | * Portions created by the Initial Developer are Copyright (C)
22 | * the Initial Developer. All Rights Reserved.
23 | *
24 | * Contributor(s):
25 | *
26 | * Anthony Minessale II
27 | * Neal Horman
28 | *
29 | *
30 | * mod_vadasr.c -- Freeswitch asr Module
31 | *
32 | */
33 | #define DR_WAV_IMPLEMENTATION
34 |
35 | #include
36 | #include "dr_wav.h"
37 | #include "opusvad.h"
38 | #include "queue.h"
39 | #include "xfasr.h"
40 |
41 | #define VAD_EVENT_START "vad::start"
42 | #define VAD_EVENT_STOP "vad::stop"
43 | #define VAD_EVENT_ASR "vad::asr"
44 |
45 | static switch_bool_t robot_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type);
46 |
47 | #define MAX_VOICE_LEN 240000
48 | #define MAX_VOICE_LEN_BASE64 645000
49 | #define MAXFILES 8
50 | #define TTS_MAX_SIZE 900
51 | #define MAX_HZ_SIZE 240
52 | #define VAD_VOICE_FRAMES 5
53 | #define VAD_SILINCE_FRAMES 50
54 | #define VAD_HIS_LEN 100
55 | #define VAD_ADD_FRAME_SIZE 5
56 |
57 | static struct {
58 | char* appid;
59 | char* appkey;
60 | } globals;
61 |
62 |
63 | typedef struct robot_session_info {
64 | int index;
65 | int filetime;
66 | int fileplaytime;
67 | int nostoptime;
68 | int asrtimeout;
69 | int asr;
70 | int play, pos;
71 | int sos, eos, ec, count;
72 | int eos_silence_threshold;
73 | int final_timeout_ms;
74 | int silence_threshold;
75 | int harmonic;
76 | int monitor;
77 | int lanid;
78 | int vadvoicems;
79 | int vadsilencems;
80 | int nslevel;
81 | switch_core_session_t *session;
82 | char taskid[32];
83 | char groupid[32];
84 | char telno[32];
85 | char userid[64];
86 | char callid[64];
87 | char orgi[64];
88 | char extid[64];
89 | char uuid[64];
90 | char uuidbak[64];
91 | char recordfilename[128];
92 | char para1[256];
93 | char para2[256];
94 | char para3[256];
95 | char filename[TTS_MAX_SIZE];
96 | char vadfilename[TTS_MAX_SIZE];
97 | short buffer[MAX_VOICE_LEN];
98 | drwav *fwav;
99 | drwav *fvadwav;
100 | int state; // 0:silence 1:voice
101 | queue *vadqueue;
102 | int16_t *vadbuffer;
103 | int16_t framecount;
104 | switch_audio_resampler_t *resampler;
105 | asr_session_t *asrsession;
106 |
107 | } robot_session_info_t;
108 |
109 |
110 | SWITCH_BEGIN_EXTERN_C
111 |
112 | SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vadasr_shutdown);
113 | SWITCH_MODULE_LOAD_FUNCTION(mod_vadasr_load);
114 | SWITCH_MODULE_DEFINITION(mod_vadasr, mod_vadasr_load, mod_vadasr_shutdown, NULL);
115 | SWITCH_STANDARD_APP(robotasr_start_function);
116 |
117 | SWITCH_MODULE_LOAD_FUNCTION(mod_vadasr_load)
118 | {
119 |
120 | switch_application_interface_t *app_interface;
121 | char *cf = "asr.conf";
122 | switch_xml_t cfg, xml, settings, param;
123 |
124 | memset(&globals, 0, sizeof(globals));
125 | globals.appid = NULL;
126 | globals.appkey = NULL;
127 |
128 | if (switch_event_reserve_subclass(VAD_EVENT_START) != SWITCH_STATUS_SUCCESS) {
129 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Robot Couldn't register subclass %s!\n",
130 | VAD_EVENT_START);
131 | return SWITCH_STATUS_TERM;
132 | }
133 |
134 | if (switch_event_reserve_subclass(VAD_EVENT_STOP) != SWITCH_STATUS_SUCCESS) {
135 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Robot Couldn't register subclass %s!\n",
136 | VAD_EVENT_STOP);
137 | return SWITCH_STATUS_TERM;
138 | }
139 |
140 | if (switch_event_reserve_subclass(VAD_EVENT_ASR) != SWITCH_STATUS_SUCCESS) {
141 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Robot Couldn't register subclass %s!\n",
142 | VAD_EVENT_ASR);
143 | return SWITCH_STATUS_TERM;
144 | }
145 |
146 | /* connect my internal structure to the blank pointer passed to me */
147 | *module_interface = switch_loadable_module_create_module_interface(pool, modname);
148 |
149 | if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
150 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
151 | }
152 | else {
153 | if ((settings = switch_xml_child(cfg, "settings"))) {
154 | for (param = switch_xml_child(settings, "param"); param; param = param->next) {
155 | char *var = (char *)switch_xml_attr_soft(param, "name");
156 | char *val = (char *)switch_xml_attr_soft(param, "value");
157 | if (!strcmp(var, "appid")) {
158 | globals.appid = val;
159 | }
160 | if (!strcmp(var, "appkey")) {
161 | globals.appkey = val;
162 | }
163 | }
164 | }
165 |
166 | switch_xml_free(xml);
167 | }
168 |
169 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Robot enabled,appid=%s,appkey=%s\n", globals.appid, globals.appkey);
170 |
171 | // 为此模块增加app,调用名称即为 vad
172 | SWITCH_ADD_APP(app_interface, "vad", "vad", "ai robot", robotasr_start_function, "[ ]", SAF_NONE);
173 |
174 | /* indicate that the module should continue to be loaded */
175 | return SWITCH_STATUS_SUCCESS;
176 | }
177 |
178 | // Called when the system shuts down
179 | SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vadasr_shutdown)
180 | {
181 | switch_event_free_subclass(VAD_EVENT_START);
182 | switch_event_free_subclass(VAD_EVENT_STOP);
183 | switch_event_free_subclass(VAD_EVENT_ASR);
184 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "myapplication disabled\n");
185 | return SWITCH_STATUS_SUCCESS;
186 | }
187 |
188 | SWITCH_STANDARD_APP(robotasr_start_function)
189 | {
190 | switch_media_bug_t *bug;
191 | switch_status_t status;
192 | switch_channel_t *channel;
193 | robot_session_info_t *robot_info;
194 |
195 | // switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "robot_start_function start\n");
196 | if (session == NULL) {
197 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR,
198 | "FreeSWITCH is NULL! Please report to developers\n");
199 | return;
200 | }
201 | channel = switch_core_session_get_channel(session);
202 | if (channel == NULL) {
203 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR,
204 | "No channel for FreeSWITCH session! Please report this "
205 | "to the developers.\n");
206 | return;
207 | }
208 |
209 | /* Is this channel already set? */
210 | bug = (switch_media_bug_t *)switch_channel_get_private(channel, "_robot_");
211 |
212 | /* If yes */
213 |
214 | if (bug != NULL) {
215 |
216 | /* If we have a stop remove audio bug */
217 | if (strcasecmp(data, "stop") == 0) {
218 | // robot_info = (robot_session_info_t *)switch_channel_get_private(channel, "_robotinfo_");
219 | switch_channel_set_private(channel, "_robot_", NULL);
220 | // process_close(robot_info);
221 | switch_core_media_bug_remove(session, &bug);
222 | return;
223 | }
224 | /* We have already started */
225 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
226 | "Robot Cannot run 2 at once on the same channel!\n");
227 | return;
228 | }
229 |
230 | const char *action = NULL, *vadvoicems = NULL, *vadsilencems = NULL, *nslevel = NULL;
231 | char *argv[4] = { 0 };
232 | char *mycmd = NULL;
233 |
234 | if (!zstr(data)) {
235 | mycmd = switch_core_session_strdup(session, data);
236 | switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
237 | }
238 |
239 | if (argv[0]) action = argv[0];
240 | if (argv[1]) vadvoicems = argv[1];
241 | if (argv[2]) vadsilencems = argv[2];
242 | if (argv[3]) nslevel = argv[3];
243 |
244 | if (!action || !vadvoicems || !vadsilencems || !nslevel) {
245 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "-ERR Missing Arguments\n");
246 | return;
247 | }
248 |
249 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
250 | "action %s vadvoicems %s vadsilencems %s nslevel %s\n", action, vadvoicems, vadsilencems,
251 | nslevel);
252 |
253 | // 初始化变量, 一定记得要 free掉
254 | robot_info = (robot_session_info_t *)malloc(sizeof(robot_session_info_t));
255 | if (robot_info == NULL) return;
256 | robot_info->session = session;
257 | strcpy(robot_info->uuid, switch_core_session_get_uuid(robot_info->session));
258 | robot_info->vadvoicems = atoi(vadvoicems);
259 | robot_info->vadsilencems = atoi(vadsilencems);
260 | robot_info->nslevel = atoi(nslevel);
261 |
262 | status = switch_core_media_bug_add(session, "vmd", NULL, robot_callback, robot_info, 0, SMBF_READ_REPLACE, &bug);
263 |
264 | if (status != SWITCH_STATUS_SUCCESS) {
265 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Robot Failure hooking to stream\n");
266 | return;
267 | }
268 | switch_channel_set_private(channel, "_robot_", bug);
269 | }
270 |
271 | SWITCH_END_EXTERN_C
272 |
273 |
274 | static switch_bool_t process_close(robot_session_info_t *rh)
275 | {
276 | switch_channel_t *channel;
277 |
278 | rh->uuid[0] = 0;
279 | rh->index = -1;
280 | if (NULL != rh->fwav) { drwav_uninit(rh->fwav); }
281 | if (NULL != rh->fvadwav) { drwav_uninit(rh->fvadwav); }
282 | destroy_queue(rh->vadqueue);
283 | channel = switch_core_session_get_channel(rh->session);
284 | switch_channel_set_private(channel, "_robot_", NULL);
285 | delete rh->asrsession;
286 | free(rh);
287 | return SWITCH_TRUE;
288 | }
289 |
290 |
291 |
292 | void handle_event(const std::string & message, void *arg)
293 | {
294 | switch_event_t *event;
295 | switch_status_t status;
296 | switch_event_t *event_copy;
297 | switch_channel_t *channel;
298 |
299 | robot_session_info_t *robot_info = (robot_session_info_t *)arg;
300 | channel = switch_core_session_get_channel(robot_info->session);
301 |
302 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "eventAsrText:%s\n", message.c_str());
303 |
304 | status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VAD_EVENT_ASR);
305 | if (status != SWITCH_STATUS_SUCCESS) { return; }
306 |
307 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Asr-Text", message.c_str());
308 | switch_channel_event_set_data(channel, event);
309 | switch_event_fire(&event);
310 | }
311 |
312 | void handle_message(const std::string & message, void *arg)
313 | {
314 | char middleText[500] = { 0 };
315 | //printf(">>> %s\n", message.c_str());
316 | cJSON* cjson_test = NULL;
317 | cJSON* cjson_action = NULL;
318 | cJSON* cjson_code = NULL;
319 | cJSON* cjson_data = NULL;
320 | cJSON* cjson_desc = NULL;
321 | cJSON* cjson_sid = NULL;
322 | cJSON* cjson_text = NULL;
323 | cJSON* cjson_segid = NULL;
324 | cJSON* cjson_cn = NULL;
325 | cJSON* cjson_st = NULL;
326 | cJSON* cjson_rt = NULL;
327 | cJSON* cjson_rt_item = NULL;
328 | cJSON* cjson_cw_item = NULL;
329 | cJSON* cjson_w_item = NULL;
330 | cJSON* cjson_type = NULL;
331 | cJSON* cjson_ws = NULL;
332 | cJSON* cjson_cw = NULL;
333 | cJSON* cjson_w = NULL;
334 |
335 | asr_session_t *asr = (asr_session_t *)arg;
336 |
337 | cjson_test = cJSON_Parse(message.c_str());
338 | cjson_action = cJSON_GetObjectItem(cjson_test, "action");
339 | cjson_code = cJSON_GetObjectItem(cjson_test, "code");
340 | cjson_data = cJSON_GetObjectItem(cjson_test, "data");
341 | cjson_desc = cJSON_GetObjectItem(cjson_test, "desc");
342 | cjson_sid = cJSON_GetObjectItem(cjson_test, "sid");
343 |
344 | if (strcmp(cjson_action->valuestring, "result") == 0 && strcmp(cjson_code->valuestring, "0") == 0 && strlen(cjson_data->valuestring) > 0)
345 | {
346 | cjson_text = cJSON_Parse(cjson_data->valuestring);
347 | cjson_segid = cJSON_GetObjectItem(cjson_text, "seg_id");
348 | cjson_cn = cJSON_GetObjectItem(cjson_text, "cn");
349 | cjson_st = cJSON_GetObjectItem(cjson_cn, "st");
350 | cjson_rt = cJSON_GetObjectItem(cjson_st, "rt");
351 | cjson_type = cJSON_GetObjectItem(cjson_st, "type");
352 |
353 | if (strcmp(cjson_type->valuestring, "0") == 0)
354 | {
355 | int rt_array_size = cJSON_GetArraySize(cjson_rt);
356 | //printf("rt_array_size:%d", rt_array_size);
357 | for (int i = 0; i < rt_array_size; i++)
358 | {
359 | cjson_rt_item = cJSON_GetArrayItem(cjson_rt, i);
360 | cjson_ws = cJSON_GetObjectItem(cjson_rt_item, "ws");
361 |
362 | int ws_array_size = cJSON_GetArraySize(cjson_ws);
363 | for (int j = 0; j < ws_array_size; j++)
364 | {
365 | cjson_cw_item = cJSON_GetArrayItem(cjson_ws, j);
366 | cjson_cw = cJSON_GetObjectItem(cjson_cw_item, "cw");
367 |
368 | int cw_array_size = cJSON_GetArraySize(cjson_cw);
369 | for (int k = 0; k < cw_array_size; k++)
370 | {
371 | cjson_w_item = cJSON_GetArrayItem(cjson_cw, k);
372 | cjson_w = cJSON_GetObjectItem(cjson_w_item, "w");
373 | //printf("w:%s", cjson_w->valuestring);
374 | if (strlen(asr->asr_text) <= BFLEN - 20)
375 | {
376 | strcat(asr->asr_text, cjson_w->valuestring);
377 | }
378 | else
379 | {
380 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "content too long!!!!!!\n");
381 | }
382 |
383 | }
384 |
385 | }
386 |
387 | }
388 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "asrFinalResult:%s\n", asr->asr_text);
389 |
390 | }
391 | else
392 | {
393 | int rt_array_size = cJSON_GetArraySize(cjson_rt);
394 | //printf("rt_array_size:%d", rt_array_size);
395 | for (int i = 0; i < rt_array_size; i++)
396 | {
397 | cjson_rt_item = cJSON_GetArrayItem(cjson_rt, i);
398 | cjson_ws = cJSON_GetObjectItem(cjson_rt_item, "ws");
399 |
400 | int ws_array_size = cJSON_GetArraySize(cjson_ws);
401 | for (int j = 0; j < ws_array_size; j++)
402 | {
403 | cjson_cw_item = cJSON_GetArrayItem(cjson_ws, j);
404 | cjson_cw = cJSON_GetObjectItem(cjson_cw_item, "cw");
405 |
406 | int cw_array_size = cJSON_GetArraySize(cjson_cw);
407 | for (int k = 0; k < cw_array_size; k++)
408 | {
409 | cjson_w_item = cJSON_GetArrayItem(cjson_cw, k);
410 | cjson_w = cJSON_GetObjectItem(cjson_w_item, "w");
411 | strcat(middleText, cjson_w->valuestring);
412 |
413 | }
414 | }
415 | }
416 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "asrTempResult:%s\n", middleText);
417 | }
418 | }
419 | else if (strcmp(cjson_action->valuestring, "error") == 0 )
420 | {
421 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asrErrorInfo:%s\n", cjson_desc->valuestring);
422 |
423 | }
424 |
425 | }
426 |
427 | static switch_bool_t robot_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
428 | {
429 | robot_session_info_t *robot_info;
430 | // switch_codec_t *read_codec;
431 | switch_frame_t *frame;
432 | int flag;
433 | drwav_data_format format;// = { 0 };
434 | int16_t len;
435 | int voiceflagcount;
436 | int silenceflagcount;
437 | int nslevel;
438 | switch_event_t *event;
439 | switch_status_t status;
440 | switch_event_t *event_copy;
441 | char *recorddir = NULL;
442 | switch_codec_implementation_t read_impl;
443 | switch_channel_t *channel;
444 |
445 |
446 |
447 | robot_info = (robot_session_info_t *)user_data;
448 | if (robot_info == NULL) { return SWITCH_FALSE; }
449 |
450 | channel = switch_core_session_get_channel(robot_info->session);
451 |
452 | voiceflagcount = robot_info->vadvoicems / 20;
453 | silenceflagcount = robot_info->vadsilencems / 20;
454 | nslevel = robot_info->nslevel;
455 |
456 | format.container = drwav_container_riff;
457 | format.format = DR_WAVE_FORMAT_PCM;
458 | format.channels = 1;
459 | format.sampleRate = (drwav_uint32)8000;
460 | format.bitsPerSample = 16;
461 |
462 | recorddir = switch_core_get_variable_dup("record_prefix");
463 |
464 | switch (type) {
465 |
466 | case SWITCH_ABC_TYPE_INIT:
467 | sprintf(robot_info->filename, "%s%s.wav", recorddir, robot_info->uuid);
468 | robot_info->fwav = drwav_open_file_write(robot_info->filename, &format);
469 | if (!robot_info->fwav) {
470 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "full record openfile error %s\n",
471 | robot_info->filename);
472 | }
473 |
474 | SetConsoleOutputCP(CP_UTF8); //解决windows控制台输出中文乱码
475 |
476 | robot_info->vadqueue = create_queue();
477 | robot_info->state = 0;
478 | robot_info->framecount = 0;
479 | robot_info->fvadwav = NULL;
480 |
481 | //初始话语音识别
482 | robot_info->asrsession = new asr_session_t();
483 | robot_info->asrsession->handle_message = handle_message;
484 | robot_info->asrsession->handle_event = handle_event;
485 | robot_info->asrsession->event_arg = robot_info;
486 |
487 | switch_core_session_get_read_impl(robot_info->session, &read_impl);
488 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Read imp %u %u.\n", read_impl.samples_per_second, read_impl.number_of_channels);
489 | status = switch_resample_create(&robot_info->resampler, read_impl.actual_samples_per_second, 16000, 640, SWITCH_RESAMPLE_QUALITY, 1);
490 | if (status != SWITCH_STATUS_SUCCESS) {
491 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to allocate resampler\n");
492 | }
493 |
494 | break;
495 |
496 | case SWITCH_ABC_TYPE_READ_REPLACE:
497 |
498 | if (robot_info->uuid[0] == 0) break;
499 |
500 | //获取语音数据
501 | frame = switch_core_media_bug_get_read_replace_frame(bug);
502 |
503 | //静音检测
504 | flag = silk_VAD_Get((const short*)frame->data);
505 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "vad result %d\n", flag);
506 |
507 | //静音标志缓冲
508 | len = get_queue_length(robot_info->vadqueue);
509 | if (len == VAD_HIS_LEN) { delete_queue(robot_info->vadqueue); }
510 | insert_queue(robot_info->vadqueue, flag, NULL, 0);
511 |
512 |
513 | //语音检测
514 | if (getvadflagcount(robot_info->vadqueue, voiceflagcount, 1) && robot_info->state == 0) {
515 |
516 | robot_info->state = 1;
517 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "+++++Speech Detected!!!+++++\n");
518 |
519 | //开启语音识别
520 | init_asr((char*)globals.appid, (char*)globals.appkey, robot_info->asrsession);
521 |
522 | sprintf(robot_info->vadfilename, "%s%s_%d.wav", recorddir, robot_info->uuid, robot_info->framecount);
523 | robot_info->fvadwav = drwav_open_file_write(robot_info->vadfilename, &format);
524 | if (!robot_info->fvadwav) {
525 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "vad open file error %s\n",
526 | robot_info->vadfilename);
527 | strcpy(robot_info->vadfilename, "");
528 | //break;
529 | }
530 |
531 |
532 | status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VAD_EVENT_START);
533 | if (status != SWITCH_STATUS_SUCCESS) { break; }
534 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Vad-Status", "start");
535 | switch_channel_event_set_data(channel, event);
536 | /*if ((switch_event_dup(&event_copy, event)) != SWITCH_STATUS_SUCCESS) { break; }
537 | switch_core_session_queue_event(robot_info->session, &event);
538 | switch_event_fire(&event_copy);*/
539 | switch_event_fire(&event);
540 | }
541 |
542 | //静音检测
543 | if (getvadflagcount(robot_info->vadqueue, silenceflagcount, 0) && robot_info->state == 1) {
544 | robot_info->state = 0;
545 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
546 | "-----Silence Detected,Stop Recording!!! FileName:%s.-----\n", robot_info->vadfilename);
547 | if (robot_info->fvadwav) { drwav_uninit(robot_info->fvadwav); }
548 | robot_info->fvadwav = NULL;
549 |
550 | status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VAD_EVENT_STOP);
551 | if (status != SWITCH_STATUS_SUCCESS) { break; }
552 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Vad-Status", "stop");
553 | switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Vad-RecordFile", robot_info->vadfilename);
554 | switch_channel_event_set_data(channel, event);
555 | switch_event_fire(&event);
556 |
557 | //发送Asr结束标记
558 | send_end(robot_info->asrsession);
559 | }
560 |
561 | //录音-vad部分
562 | if (robot_info->fvadwav) { drwav_write_pcm_frames(robot_info->fvadwav, frame->samples, frame->data); }
563 | //完整部分
564 | if (robot_info->fwav){ drwav_write_pcm_frames(robot_info->fwav, frame->samples, frame->data); }
565 | robot_info->framecount++;
566 |
567 | //检测到语音时发送语音数据包
568 | if(robot_info->state == 1)
569 | {
570 | //上采样至16K
571 | switch_resample_process(robot_info->resampler, (int16_t *)frame->data, frame->datalen);
572 | send_data(robot_info->asrsession, (char*)robot_info->resampler->to, robot_info->resampler->to_len);
573 | }
574 | break;
575 |
576 | case SWITCH_ABC_TYPE_CLOSE:
577 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SWITCH_ABC_TYPE_CLOSE\n");
578 | send_end(robot_info->asrsession);
579 | thrd_join(robot_info->asrsession->thr, NULL);
580 | thrd_detach(robot_info->asrsession->thr);
581 | mtx_destroy(&robot_info->asrsession->mutex);
582 |
583 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "the asr thread closed!!!\n");
584 |
585 | if (robot_info->resampler)
586 | {
587 | switch_resample_destroy(&robot_info->resampler);
588 | }
589 | process_close(robot_info);
590 | break;
591 | default:
592 | break;
593 | }
594 |
595 | switch_safe_free(recorddir);
596 | return SWITCH_TRUE;
597 | }
598 |
599 |
--------------------------------------------------------------------------------
/mod_vadasr使用说明.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanghaimoon888/mod_vadasr/900752f40bcd8e182e6967566c061547a1be31a4/mod_vadasr使用说明.docx
--------------------------------------------------------------------------------
/opusvad.h:
--------------------------------------------------------------------------------
1 | //#include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | int silk_VAD_Get(
8 | // int state, /* Encoder state */
9 | const short pIn[] /* I PCM input */
10 | );
11 |
12 | #define TYPE_NO_VOICE_ACTIVITY 0
13 | #define TYPE_UNVOICED 1
14 | #define TYPE_VOICED 2
15 |
16 | #define SPEECH_ACTIVITY_DTX_THRES 0.05f
17 | #define SILK_FIX_CONST(C, Q) ((int)((C) * ((long)1 << (Q)) + 0.5))
18 | #define silk_int16_MAX 0x7FFF /* 2^15 - 1 = 32767 */
19 | #define silk_int16_MIN ((short)0x8000) /* -2^15 = -32768 */
20 | #define silk_int32_MAX 0x7FFFFFFF /* 2^31 - 1 = 2147483647 */
21 | #define silk_int32_MIN ((int)0x80000000) /* -2^31 = -2147483648 */
22 | #define silk_memset(dest, src, size) memset((dest), (src), (size))
23 |
24 | #define VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 1024 /* Must be < 4096 */
25 | #define VAD_NOISE_LEVELS_BIAS 50
26 |
27 | /* Sigmoid settings */
28 | #define VAD_NEGATIVE_OFFSET_Q5 128 /* sigmoid is 0 at -128 */
29 | #define VAD_SNR_FACTOR_Q16 45000
30 |
31 | /* smoothing for SNR measurement */
32 | #define VAD_SNR_SMOOTH_COEF_Q18 4096
33 |
34 | #define VAD_N_BANDS 4
35 | #define VAD_INTERNAL_SUBFRAMES_LOG2 2
36 | #define VAD_INTERNAL_SUBFRAMES (1 << VAD_INTERNAL_SUBFRAMES_LOG2)
37 | #define silk_uint8_MAX 0xFF /* 2^8 - 1 = 255 */
38 |
39 | #define VARDECL(type, var) type *var
40 | #define silk_RSHIFT32(a, shift) ((a) >> (shift))
41 | #define silk_RSHIFT(a, shift) ((a) >> (shift))
42 | #define silk_LSHIFT32(a, shift) ((a) << (shift))
43 | #define silk_LSHIFT(a, shift) ((a) << (shift))
44 | #define ALLOC(var, size, type) var = ((type *)alloca(sizeof(type) * (size)))
45 | #define silk_ADD16(a, b) ((a) + (b))
46 | #define silk_ADD32(a, b) ((a) + (b))
47 | #define silk_ADD64(a, b) ((a) + (b))
48 |
49 | #define silk_SUB16(a, b) ((a) - (b))
50 | #define silk_SUB32(a, b) ((a) - (b))
51 | #define silk_SUB64(a, b) ((a) - (b))
52 | #define silk_SMULWB(a32, b32) \
53 | ((((a32) >> 16) * (int)((short)(b32))) + ((((a32)&0x0000FFFF) * (int)((short)(b32))) >> 16))
54 | #define silk_SMLAWB(a32, b32, c32) \
55 | ((a32) + ((((b32) >> 16) * (int)((short)(c32))) + ((((b32)&0x0000FFFF) * (int)((short)(c32))) >> 16)))
56 | #define silk_SAT16(a) ((a) > silk_int16_MAX ? silk_int16_MAX : ((a) < silk_int16_MIN ? silk_int16_MIN : (a)))
57 | #define silk_MLA(a32, b32, c32) silk_ADD32((a32), ((b32) * (c32)))
58 | #define silk_SMLABB(a32, b32, c32) ((a32) + ((int)((short)(b32))) * (int)((short)(c32)))
59 | #define silk_ADD_POS_SAT32(a, b) ((((unsigned int)(a) + (unsigned int)(b)) & 0x80000000) ? silk_int32_MAX : ((a) + (b)))
60 | #define silk_ADD_POS_SAT32(a, b) ((((unsigned int)(a) + (unsigned int)(b)) & 0x80000000) ? silk_int32_MAX : ((a) + (b)))
61 | #define silk_DIV32_16(a32, b16) ((int)((a32) / (b16)))
62 | #define silk_DIV32(a32, b32) ((int)((a32) / (b32)))
63 | #define silk_RSHIFT_ROUND(a, shift) ((shift) == 1 ? ((a) >> 1) + ((a)&1) : (((a) >> ((shift)-1)) + 1) >> 1)
64 |
65 | #define silk_SMULWW(a32, b32) silk_MLA(silk_SMULWB((a32), (b32)), (a32), silk_RSHIFT_ROUND((b32), 16))
66 | #define silk_min(a, b) (((a) < (b)) ? (a) : (b))
67 | #define silk_max(a, b) (((a) > (b)) ? (a) : (b))
68 | #define silk_ADD_LSHIFT32(a, b, shift) silk_ADD32((a), silk_LSHIFT32((b), (shift))) /* shift >= 0 */
69 | #define silk_MUL(a32, b32) ((a32) * (b32))
70 | #define silk_SMULBB(a32, b32) ((int)((short)(a32)) * (int)((short)(b32)))
71 | #define silk_LIMIT(a, limit1, limit2) \
72 | ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) \
73 | : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a))))
74 |
75 | #define silk_LSHIFT_SAT32(a, shift) \
76 | (silk_LSHIFT32(silk_LIMIT((a), silk_RSHIFT32(silk_int32_MIN, (shift)), silk_RSHIFT32(silk_int32_MAX, (shift))), \
77 | (shift)))
78 |
79 | static const int tiltWeights[VAD_N_BANDS] = {30000, 6000, -12000, -12000};
80 | static const int sigm_LUT_neg_Q15[6] = {16384, 8812, 3906, 1554, 589, 219};
81 | static const int sigm_LUT_slope_Q10[6] = {237, 153, 73, 30, 12, 7};
82 | static const int sigm_LUT_pos_Q15[6] = {16384, 23955, 28861, 31213, 32178, 32548};
83 |
84 | static __inline int ec_bsr(unsigned long _x)
85 | {
86 | //return __builtin_clz(_x);
87 | unsigned long ret;
88 | _BitScanReverse(&ret, _x);
89 | return (int)ret;
90 | }
91 | #define EC_CLZ0 (1)
92 | #define EC_CLZ(_x) (-ec_bsr(_x))
93 | #define EC_ILOG(_x) (EC_CLZ0 - EC_CLZ(_x))
94 | static int silk_min_int(int a, int b) { return (((a) < (b)) ? (a) : (b)); }
95 | static int silk_max_int(int a, int b) { return (((a) > (b)) ? (a) : (b)); }
96 | static int silk_max_32(int a, int b) { return (((a) > (b)) ? (a) : (b)); }
97 | static int silk_CLZ32(int in32) { return in32 ? 32 - EC_ILOG(in32) : 32; }
98 | static int silk_ROR32(int a32, int rot)
99 | {
100 | unsigned int x = (unsigned int)a32;
101 | unsigned int r = (unsigned int)rot;
102 | unsigned int m = (unsigned int)-rot;
103 | if (rot == 0) {
104 | return a32;
105 | } else if (rot < 0) {
106 | return (int)((x << m) | (x >> (32 - m)));
107 | } else {
108 | return (int)((x << (32 - r)) | (x >> r));
109 | }
110 | }
111 | static void silk_CLZ_FRAC(int in, /* I input */
112 | int *lz, /* O number of leading zeros */
113 | int *frac_Q7 /* O the 7 bits right after the leading one */
114 | )
115 | {
116 | int lzeros = silk_CLZ32(in);
117 |
118 | *lz = lzeros;
119 | *frac_Q7 = silk_ROR32(in, 24 - lzeros) & 0x7f;
120 | }
121 |
122 | /* Approximation of square root */
123 | /* Accuracy: < +/- 10% for output values > 15 */
124 | /* < +/- 2.5% for output values > 120 */
125 | static int silk_SQRT_APPROX(int x)
126 | {
127 | int y, lz, frac_Q7;
128 |
129 | if (x <= 0) { return 0; }
130 |
131 | silk_CLZ_FRAC(x, &lz, &frac_Q7);
132 |
133 | if (lz & 1) {
134 | y = 32768;
135 | } else {
136 | y = 46214; /* 46214 = sqrt(2) * 32768 */
137 | }
138 |
139 | /* get scaling right */
140 | y >>= silk_RSHIFT(lz, 1);
141 |
142 | /* increment using fractional part of input */
143 | y = silk_SMLAWB(y, y, silk_SMULBB(213, frac_Q7));
144 |
145 | return y;
146 | }
147 |
148 | static short A_fb1_20 = 5394 << 1;
149 | static short A_fb1_21 = -24290; /* (int16)(20623 << 1) */
150 |
151 | typedef struct {
152 | int AnaState[2]; /* Analysis filterbank state: 0-8 kHz */
153 | int AnaState1[2]; /* Analysis filterbank state: 0-4 kHz */
154 | int AnaState2[2]; /* Analysis filterbank state: 0-2 kHz */
155 | int XnrgSubfr[4]; /* Subframe energies */
156 | int NrgRatioSmth_Q8[VAD_N_BANDS]; /* Smoothed energy level in each band */
157 | short HPstate; /* State of differentiator in the lowest band */
158 | int NL[VAD_N_BANDS]; /* Noise energy level in each band */
159 | int inv_NL[VAD_N_BANDS]; /* Inverse noise energy level in each band */
160 | int NoiseLevelBias[VAD_N_BANDS]; /* Noise level estimator bias/offset */
161 | int counter; /* Frame counter used in the initial phase */
162 | } VAD_state;
163 |
164 | /* Split signal into two decimated bands using first-order allpass filters */
165 | void silk_ana_filt_bank_1(const short *in, /* I Input signal [N] */
166 | int *S, /* I/O State vector [2] */
167 | short *outL, /* O Low band [N/2] */
168 | short *outH, /* O High band [N/2] */
169 | const int N /* I Number of input samples */
170 | )
171 | {
172 | int k, N2 = silk_RSHIFT(N, 1);
173 | int in32, X, Y, out_1, out_2;
174 |
175 | /* Internal variables and state are in Q10 format */
176 | for (k = 0; k < N2; k++) {
177 | /* Convert to Q10 */
178 | in32 = silk_LSHIFT((int)in[2 * k], 10);
179 |
180 | /* All-pass section for even input sample */
181 | Y = silk_SUB32(in32, S[0]);
182 | X = silk_SMLAWB(Y, Y, A_fb1_21);
183 | out_1 = silk_ADD32(S[0], X);
184 | S[0] = silk_ADD32(in32, X);
185 |
186 | /* Convert to Q10 */
187 | in32 = silk_LSHIFT((int)in[2 * k + 1], 10);
188 |
189 | /* All-pass section for odd input sample, and add to output of previous section */
190 | Y = silk_SUB32(in32, S[1]);
191 | X = silk_SMULWB(Y, A_fb1_20);
192 | out_2 = silk_ADD32(S[1], X);
193 | S[1] = silk_ADD32(in32, X);
194 |
195 | /* Add/subtract, convert back to int16 and store to output */
196 | outL[k] = (short)silk_SAT16(silk_RSHIFT_ROUND(silk_ADD32(out_2, out_1), 11));
197 | outH[k] = (short)silk_SAT16(silk_RSHIFT_ROUND(silk_SUB32(out_2, out_1), 11));
198 | }
199 | }
200 |
201 | void silk_VAD_GetNoiseLevels(const int pX[VAD_N_BANDS], /* I subband energies */
202 | VAD_state *psSilk_VAD /* I/O Pointer to Silk VAD state */
203 | )
204 | {
205 | int k;
206 | int nl, nrg, inv_nrg;
207 | int coef, min_coef;
208 |
209 | /* Initially faster smoothing */
210 | if (psSilk_VAD->counter < 1000) { /* 1000 = 20 sec */
211 | min_coef = silk_DIV32_16(silk_int16_MAX, silk_RSHIFT(psSilk_VAD->counter, 4) + 1);
212 | } else {
213 | min_coef = 0;
214 | }
215 |
216 | for (k = 0; k < VAD_N_BANDS; k++) {
217 | /* Get old noise level estimate for current band */
218 | nl = psSilk_VAD->NL[k];
219 | // silk_assert(nl >= 0);
220 |
221 | /* Add bias */
222 | nrg = silk_ADD_POS_SAT32(pX[k], psSilk_VAD->NoiseLevelBias[k]);
223 | // silk_assert(nrg > 0);
224 |
225 | /* Invert energies */
226 | inv_nrg = silk_DIV32(silk_int32_MAX, nrg);
227 | // silk_assert(inv_nrg >= 0);
228 |
229 | /* Less update when subband energy is high */
230 | if (nrg > silk_LSHIFT(nl, 3)) {
231 | coef = VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 >> 3;
232 | } else if (nrg < nl) {
233 | coef = VAD_NOISE_LEVEL_SMOOTH_COEF_Q16;
234 | } else {
235 | coef = silk_SMULWB(silk_SMULWW(inv_nrg, nl), VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 << 1);
236 | }
237 |
238 | /* Initially faster smoothing */
239 | coef = silk_max_int(coef, min_coef);
240 |
241 | /* Smooth inverse energies */
242 | psSilk_VAD->inv_NL[k] = silk_SMLAWB(psSilk_VAD->inv_NL[k], inv_nrg - psSilk_VAD->inv_NL[k], coef);
243 | // silk_assert(psSilk_VAD->inv_NL[k] >= 0);
244 |
245 | /* Compute noise level by inverting again */
246 | nl = silk_DIV32(silk_int32_MAX, psSilk_VAD->inv_NL[k]);
247 | // silk_assert(nl >= 0);
248 |
249 | /* Limit noise levels (guarantee 7 bits of head room) */
250 | nl = silk_min(nl, 0x00FFFFFF);
251 |
252 | /* Store as part of state */
253 | psSilk_VAD->NL[k] = nl;
254 | }
255 |
256 | /* Increment frame counter */
257 | psSilk_VAD->counter++;
258 | }
259 |
260 | int silk_lin2log(const int inLin /* I input in linear scale */
261 | )
262 | {
263 | int lz, frac_Q7;
264 |
265 | silk_CLZ_FRAC(inLin, &lz, &frac_Q7);
266 |
267 | /* Piece-wise parabolic approximation */
268 | return silk_ADD_LSHIFT32(silk_SMLAWB(frac_Q7, silk_MUL(frac_Q7, 128 - frac_Q7), 179), 31 - lz, 7);
269 | }
270 |
271 | int silk_sigm_Q15(int in_Q5 /* I */
272 | )
273 | {
274 | int ind;
275 |
276 | if (in_Q5 < 0) {
277 | /* Negative input */
278 | in_Q5 = -in_Q5;
279 | if (in_Q5 >= 6 * 32) {
280 | return 0; /* Clip */
281 | } else {
282 | /* Linear interpolation of look up table */
283 | ind = silk_RSHIFT(in_Q5, 5);
284 | return (sigm_LUT_neg_Q15[ind] - silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F));
285 | }
286 | } else {
287 | /* Positive input */
288 | if (in_Q5 >= 6 * 32) {
289 | return 32767; /* clip */
290 | } else {
291 | /* Linear interpolation of look up table */
292 | ind = silk_RSHIFT(in_Q5, 5);
293 | return (sigm_LUT_pos_Q15[ind] + silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F));
294 | }
295 | }
296 | }
297 | int silk_VAD_Init( /* O Return value, 0 if success */
298 | VAD_state *psSilk_VAD /* I/O Pointer to Silk VAD state */
299 | )
300 | {
301 | int b, ret = 0;
302 |
303 | /* reset state memory */
304 | silk_memset(psSilk_VAD, 0, sizeof(VAD_state));
305 |
306 | /* init noise levels */
307 | /* Initialize array with approx pink noise levels (psd proportional to inverse of frequency) */
308 | for (b = 0; b < VAD_N_BANDS; b++) {
309 | psSilk_VAD->NoiseLevelBias[b] = silk_max_32(silk_DIV32_16(VAD_NOISE_LEVELS_BIAS, b + 1), 1);
310 | }
311 |
312 | /* Initialize state */
313 | for (b = 0; b < VAD_N_BANDS; b++) {
314 | psSilk_VAD->NL[b] = silk_MUL(100, psSilk_VAD->NoiseLevelBias[b]);
315 | psSilk_VAD->inv_NL[b] = silk_DIV32(silk_int32_MAX, psSilk_VAD->NL[b]);
316 | }
317 | psSilk_VAD->counter = 15;
318 |
319 | /* init smoothed energy-to-noise ratio*/
320 | for (b = 0; b < VAD_N_BANDS; b++) { psSilk_VAD->NrgRatioSmth_Q8[b] = 100 * 256; /* 100 * 256 --> 20 dB SNR */ }
321 |
322 | return (ret);
323 | }
324 |
325 | // static int noSpeechCounter;
326 |
327 | int silk_VAD_Get(
328 | // int state, /* Encoder state */
329 | const short pIn[] /* I PCM input */
330 | )
331 | {
332 | int SA_Q15, pSNR_dB_Q7, input_tilt;
333 | int decimated_framelength1, decimated_framelength2;
334 | int decimated_framelength;
335 | int dec_subframe_length, dec_subframe_offset, SNR_Q7, i, b, s;
336 | int sumSquared, smooth_coef_Q16;
337 | short HPstateTmp;
338 | VARDECL(short, X);
339 | int Xnrg[4];
340 | int NrgToNoiseRatio_Q8[4];
341 | int speech_nrg, x_tmp;
342 | int X_offset[4];
343 | // int ret = 0;
344 | int frame_length = 20; //
345 | int fs_kHz = 8;
346 | int input_quality_bands_Q15[VAD_N_BANDS];
347 | int signalType;
348 | int VAD_flag;
349 | VAD_state *psSilk_VAD;
350 | int ret1;
351 | int input_tilt_Q15;
352 | int speech_activity_Q8;
353 | /* Safety checks
354 | silk_assert(4 == 4);
355 | silk_assert(MAX_FRAME_LENGTH >= frame_length);
356 | silk_assert(frame_length <= 512);
357 | silk_assert(frame_length == 8 * silk_RSHIFT(frame_length, 3));
358 | */
359 | /***********************/
360 | /* Filter and Decimate */
361 | /***********************/
362 | decimated_framelength1 = silk_RSHIFT(frame_length, 1);
363 | decimated_framelength2 = silk_RSHIFT(frame_length, 2);
364 | decimated_framelength = silk_RSHIFT(frame_length, 3);
365 | /* Decimate into 4 bands:
366 | 0 L 3L L 3L 5L
367 | - -- - -- --
368 | 8 8 2 4 4
369 |
370 | [0-1 kHz| temp. |1-2 kHz| 2-4 kHz | 4-8 kHz |
371 |
372 | They're arranged to allow the minimal ( frame_length / 4 ) extra
373 | scratch space during the downsampling process */
374 | X_offset[0] = 0;
375 | X_offset[1] = decimated_framelength + decimated_framelength2;
376 | X_offset[2] = X_offset[1] + decimated_framelength;
377 | X_offset[3] = X_offset[2] + decimated_framelength2;
378 | ALLOC(X, X_offset[3] + decimated_framelength1, short);
379 |
380 | psSilk_VAD = (VAD_state *)malloc(sizeof(VAD_state));
381 | ret1 = silk_VAD_Init(psSilk_VAD);
382 |
383 | /* 0-8 kHz to 0-4 kHz and 4-8 kHz */
384 | silk_ana_filt_bank_1(pIn, &psSilk_VAD->AnaState[0], X, &X[X_offset[3]], frame_length);
385 |
386 | /* 0-4 kHz to 0-2 kHz and 2-4 kHz */
387 | silk_ana_filt_bank_1(X, &psSilk_VAD->AnaState1[0], X, &X[X_offset[2]], decimated_framelength1);
388 |
389 | /* 0-2 kHz to 0-1 kHz and 1-2 kHz */
390 | silk_ana_filt_bank_1(X, &psSilk_VAD->AnaState2[0], X, &X[X_offset[1]], decimated_framelength2);
391 |
392 | /*********************************************/
393 | /* HP filter on lowest band (differentiator) */
394 | /*********************************************/
395 | X[decimated_framelength - 1] = silk_RSHIFT(X[decimated_framelength - 1], 1);
396 | HPstateTmp = X[decimated_framelength - 1];
397 | for (i = decimated_framelength - 1; i > 0; i--) {
398 | X[i - 1] = silk_RSHIFT(X[i - 1], 1);
399 | X[i] -= X[i - 1];
400 | }
401 | X[0] -= psSilk_VAD->HPstate;
402 | psSilk_VAD->HPstate = HPstateTmp;
403 |
404 | /*************************************/
405 | /* Calculate the energy in each band */
406 | /*************************************/
407 | for (b = 0; b < 4; b++) {
408 | /* Find the decimated framelength in the non-uniformly divided bands */
409 | decimated_framelength = silk_RSHIFT(frame_length, silk_min_int(4 - b, 4 - 1));
410 |
411 | /* Split length into subframe lengths */
412 | dec_subframe_length = silk_RSHIFT(decimated_framelength, VAD_INTERNAL_SUBFRAMES_LOG2);
413 | dec_subframe_offset = 0;
414 |
415 | /* Compute energy per sub-frame */
416 | /* initialize with summed energy of last subframe */
417 | Xnrg[b] = psSilk_VAD->XnrgSubfr[b];
418 | for (s = 0; s < VAD_INTERNAL_SUBFRAMES; s++) {
419 | sumSquared = 0;
420 | for (i = 0; i < dec_subframe_length; i++) {
421 | /* The energy will be less than dec_subframe_length * ( silk_short_MIN / 8 ) ^ 2. */
422 | /* Therefore we can accumulate with no risk of overflow (unless dec_subframe_length > 128) */
423 | x_tmp = silk_RSHIFT(X[X_offset[b] + i + dec_subframe_offset], 3);
424 | sumSquared = silk_SMLABB(sumSquared, x_tmp, x_tmp);
425 |
426 | /* Safety check */
427 | // silk_assert(sumSquared >= 0);
428 | }
429 |
430 | /* Add/saturate summed energy of current subframe */
431 | if (s < VAD_INTERNAL_SUBFRAMES - 1) {
432 | Xnrg[b] = silk_ADD_POS_SAT32(Xnrg[b], sumSquared);
433 | } else {
434 | /* Look-ahead subframe */
435 | Xnrg[b] = silk_ADD_POS_SAT32(Xnrg[b], silk_RSHIFT(sumSquared, 1));
436 | }
437 |
438 | dec_subframe_offset += dec_subframe_length;
439 | }
440 | psSilk_VAD->XnrgSubfr[b] = sumSquared;
441 | }
442 |
443 | /********************/
444 | /* Noise estimation */
445 | /********************/
446 | silk_VAD_GetNoiseLevels(&Xnrg[0], psSilk_VAD);
447 |
448 | /***********************************************/
449 | /* Signal-plus-noise to noise ratio estimation */
450 | /***********************************************/
451 | sumSquared = 0;
452 | input_tilt = 0;
453 | for (b = 0; b < 4; b++) {
454 | speech_nrg = Xnrg[b] - psSilk_VAD->NL[b];
455 | if (speech_nrg > 0) {
456 | /* Divide, with sufficient resolution */
457 | if ((Xnrg[b] & 0xFF800000) == 0) {
458 | NrgToNoiseRatio_Q8[b] = silk_DIV32(silk_LSHIFT(Xnrg[b], 8), psSilk_VAD->NL[b] + 1);
459 | } else {
460 | NrgToNoiseRatio_Q8[b] = silk_DIV32(Xnrg[b], silk_RSHIFT(psSilk_VAD->NL[b], 8) + 1);
461 | }
462 |
463 | /* Convert to log domain */
464 | SNR_Q7 = silk_lin2log(NrgToNoiseRatio_Q8[b]) - 8 * 128;
465 |
466 | /* Sum-of-squares */
467 | sumSquared = silk_SMLABB(sumSquared, SNR_Q7, SNR_Q7); /* Q14 */
468 |
469 | /* Tilt measure */
470 | if (speech_nrg < ((int)1 << 20)) {
471 | /* Scale down SNR value for small subband speech energies */
472 | SNR_Q7 = silk_SMULWB(silk_LSHIFT(silk_SQRT_APPROX(speech_nrg), 6), SNR_Q7);
473 | }
474 | input_tilt = silk_SMLAWB(input_tilt, tiltWeights[b], SNR_Q7);
475 | } else {
476 | NrgToNoiseRatio_Q8[b] = 256;
477 | }
478 | }
479 |
480 | /* Mean-of-squares */
481 | sumSquared = silk_DIV32_16(sumSquared, 4); /* Q14 */
482 |
483 | /* Root-mean-square approximation, scale to dBs, and write to output pointer */
484 | pSNR_dB_Q7 = (short)(3 * silk_SQRT_APPROX(sumSquared)); /* Q7 */
485 |
486 | /*********************************/
487 | /* Speech Probability Estimation */
488 | /*********************************/
489 | SA_Q15 = silk_sigm_Q15(silk_SMULWB(VAD_SNR_FACTOR_Q16, pSNR_dB_Q7) - VAD_NEGATIVE_OFFSET_Q5);
490 |
491 | /**************************/
492 | /* Frequency Tilt Measure */
493 | /**************************/
494 | input_tilt_Q15 = silk_LSHIFT(silk_sigm_Q15(input_tilt) - 16384, 1);
495 |
496 | /**************************************************/
497 | /* Scale the sigmoid output based on power levels */
498 | /**************************************************/
499 | speech_nrg = 0;
500 |
501 | for (b = 0; b < 4; b++) {
502 | /* Accumulate signal-without-noise energies, higher frequency bands have more weight */
503 | speech_nrg += (b + 1) * silk_RSHIFT(Xnrg[b] - psSilk_VAD->NL[b], 4);
504 | }
505 |
506 | /* Power scaling */
507 | if (speech_nrg <= 0) {
508 | SA_Q15 = silk_RSHIFT(SA_Q15, 1);
509 | } else if (speech_nrg < 32768) {
510 | if (frame_length == 10 * fs_kHz) {
511 | speech_nrg = silk_LSHIFT_SAT32(speech_nrg, 16);
512 | } else {
513 | speech_nrg = silk_LSHIFT_SAT32(speech_nrg, 15);
514 | }
515 |
516 | /* square-root */
517 | speech_nrg = silk_SQRT_APPROX(speech_nrg);
518 | SA_Q15 = silk_SMULWB(32768 + speech_nrg, SA_Q15);
519 | }
520 |
521 | /* Copy the resulting speech activity in Q8 */
522 | speech_activity_Q8 = silk_min_int(silk_RSHIFT(SA_Q15, 7), silk_uint8_MAX);
523 |
524 | /***********************************/
525 | /* Energy Level and SNR estimation */
526 | /***********************************/
527 | /* Smoothing coefficient */
528 | smooth_coef_Q16 = silk_SMULWB(VAD_SNR_SMOOTH_COEF_Q18, silk_SMULWB((int)SA_Q15, SA_Q15));
529 |
530 | if (frame_length == 10 * fs_kHz) { smooth_coef_Q16 >>= 1; }
531 |
532 | for (b = 0; b < 4; b++) {
533 | /* compute smoothed energy-to-noise ratio per band */
534 | psSilk_VAD->NrgRatioSmth_Q8[b] = silk_SMLAWB(
535 | psSilk_VAD->NrgRatioSmth_Q8[b], NrgToNoiseRatio_Q8[b] - psSilk_VAD->NrgRatioSmth_Q8[b], smooth_coef_Q16);
536 |
537 | /* signal to noise ratio in dB per band */
538 | SNR_Q7 = 3 * (silk_lin2log(psSilk_VAD->NrgRatioSmth_Q8[b]) - 8 * 128);
539 | /* quality = sigmoid( 0.25 * ( SNR_dB - 16 ) ); */
540 | input_quality_bands_Q15[b] = silk_sigm_Q15(silk_RSHIFT(SNR_Q7 - 16 * 128, 4));
541 | }
542 | // gap************************************************************//
543 | if (speech_activity_Q8 < SILK_FIX_CONST(SPEECH_ACTIVITY_DTX_THRES, 8)) {
544 | signalType = TYPE_NO_VOICE_ACTIVITY;
545 | // noSpeechCounter++;
546 | VAD_flag = 0;
547 | } else {
548 | signalType = TYPE_UNVOICED;
549 | VAD_flag = 1;
550 | }
551 | free(psSilk_VAD);
552 | return (VAD_flag);
553 | }
--------------------------------------------------------------------------------
/queue.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | typedef struct _node {
4 | int data;
5 | void *buffer;
6 | struct _node *next;
7 | // struct _node *pre;
8 | } node;
9 |
10 | typedef struct {
11 | node *front;
12 | node *rear;
13 | } queue;
14 |
15 | queue *create_queue(void)
16 | {
17 | queue *myqueue = (queue *)malloc(sizeof(queue));
18 | myqueue->front = NULL;
19 | myqueue->rear = NULL;
20 |
21 | return myqueue;
22 | }
23 |
24 | // insert queue
25 | queue *insert_queue(queue *myqueue, int data, void *buffer, int buflen)
26 | {
27 | if (NULL == myqueue) {
28 | printf("myqueue is NULL\n");
29 | return NULL;
30 | }
31 | node *new_node = NULL;
32 | new_node = (node *)malloc(sizeof(node)); // create a new node
33 | new_node->data = data;
34 | if (NULL != buffer) {
35 | new_node->buffer = malloc(buflen);
36 | memcpy(new_node->buffer, buffer, buflen);
37 | }
38 | else
39 | {
40 | new_node->buffer = NULL;
41 | }
42 | new_node->next = NULL;
43 |
44 | if (myqueue->rear == NULL) { // if the queue is empty
45 | myqueue->front = myqueue->rear = new_node;
46 | } else {
47 | myqueue->rear->next = new_node;
48 | myqueue->rear = new_node; // move queue rear pointer to new_node
49 | }
50 |
51 | return myqueue;
52 | }
53 |
54 | queue *delete_queue(queue *myqueue)
55 | {
56 | node *p_node = NULL;
57 | p_node = myqueue->front;
58 | if (NULL == p_node) {
59 | printf("this is empty queue\n");
60 | return NULL;
61 | } else {
62 | myqueue->front = myqueue->front->next;
63 | if (myqueue->front == NULL) { myqueue->rear = NULL; }
64 | if (NULL != p_node->buffer) { free(p_node->buffer); }
65 | free(p_node);
66 | }
67 |
68 | return myqueue;
69 | }
70 |
71 | int get_queue_length(queue *myqueue)
72 | {
73 | node *p_node = NULL;
74 | int len = 0;
75 |
76 | p_node = myqueue->front;
77 | if (NULL != p_node) { len = 1; }
78 | while (p_node != myqueue->rear) {
79 | p_node = p_node->next;
80 | len++;
81 | }
82 |
83 | return len;
84 | }
85 |
86 | void destroy_queue(queue *myqueue)
87 | {
88 | int len = get_queue_length(myqueue);
89 | for (int i = 0; i < len; i++) { delete_queue(myqueue); }
90 |
91 | free(myqueue);
92 | }
93 |
94 | void queue_print(queue *myqueue)
95 | {
96 | node *p_node = NULL;
97 | p_node = myqueue->front;
98 |
99 | if (NULL == p_node) {
100 | printf("this is empty queue\n");
101 | return;
102 | }
103 | printf("The queue is :");
104 | while (p_node != myqueue->rear) {
105 | printf("%2d", p_node->data);
106 | p_node = p_node->next;
107 | }
108 | printf("%2d\n", p_node->data);
109 | }
110 | //typedef enum { false, true } bool;
111 | bool getvadflagcount(queue *myqueue, int length, int state)
112 | {
113 | int len = get_queue_length(myqueue);
114 | len = len - length;
115 | if (len < 0) { return false; }
116 |
117 | node *p_node = NULL;
118 | p_node = myqueue->front;
119 |
120 | while (p_node != myqueue->rear) {
121 | if (len == 0) {
122 | if (p_node->data != state) { return false; }
123 | } else {
124 | len--;
125 | }
126 | p_node = p_node->next;
127 | }
128 |
129 | /*int count = 0;
130 | while (p_node != myqueue->rear) {
131 |
132 | count++;
133 | if (count > len && p_node->data != state) { return false; }
134 | p_node = p_node->next;
135 | }*/
136 |
137 | return true;
138 | }
139 |
--------------------------------------------------------------------------------
/tinycthread.c:
--------------------------------------------------------------------------------
1 | /* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
2 | Copyright (c) 2012 Marcus Geelnard
3 | Copyright (c) 2013-2016 Evan Nemerson
4 |
5 | This software is provided 'as-is', without any express or implied
6 | warranty. In no event will the authors be held liable for any damages
7 | arising from the use of this software.
8 |
9 | Permission is granted to anyone to use this software for any purpose,
10 | including commercial applications, and to alter it and redistribute it
11 | freely, subject to the following restrictions:
12 |
13 | 1. The origin of this software must not be misrepresented; you must not
14 | claim that you wrote the original software. If you use this software
15 | in a product, an acknowledgment in the product documentation would be
16 | appreciated but is not required.
17 |
18 | 2. Altered source versions must be plainly marked as such, and must not be
19 | misrepresented as being the original software.
20 |
21 | 3. This notice may not be removed or altered from any source
22 | distribution.
23 | */
24 |
25 | #include "tinycthread.h"
26 | #include
27 |
28 | /* Platform specific includes */
29 | #if defined(_TTHREAD_POSIX_)
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #elif defined(_TTHREAD_WIN32_)
36 | #include
37 | #include
38 | #endif
39 |
40 | /* Standard, good-to-have defines */
41 | #ifndef NULL
42 | #define NULL (void*)0
43 | #endif
44 | #ifndef TRUE
45 | #define TRUE 1
46 | #endif
47 | #ifndef FALSE
48 | #define FALSE 0
49 | #endif
50 |
51 | #ifdef __cplusplus
52 | extern "C" {
53 | #endif
54 |
55 |
56 | int mtx_init(mtx_t *mtx, int type)
57 | {
58 | #if defined(_TTHREAD_WIN32_)
59 | mtx->mAlreadyLocked = FALSE;
60 | mtx->mRecursive = type & mtx_recursive;
61 | mtx->mTimed = type & mtx_timed;
62 | if (!mtx->mTimed)
63 | {
64 | InitializeCriticalSection(&(mtx->mHandle.cs));
65 | }
66 | else
67 | {
68 | mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL);
69 | if (mtx->mHandle.mut == NULL)
70 | {
71 | return thrd_error;
72 | }
73 | }
74 | return thrd_success;
75 | #else
76 | int ret;
77 | pthread_mutexattr_t attr;
78 | pthread_mutexattr_init(&attr);
79 | if (type & mtx_recursive)
80 | {
81 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
82 | }
83 | ret = pthread_mutex_init(mtx, &attr);
84 | pthread_mutexattr_destroy(&attr);
85 | return ret == 0 ? thrd_success : thrd_error;
86 | #endif
87 | }
88 |
89 | void mtx_destroy(mtx_t *mtx)
90 | {
91 | #if defined(_TTHREAD_WIN32_)
92 | if (!mtx->mTimed)
93 | {
94 | DeleteCriticalSection(&(mtx->mHandle.cs));
95 | }
96 | else
97 | {
98 | CloseHandle(mtx->mHandle.mut);
99 | }
100 | #else
101 | pthread_mutex_destroy(mtx);
102 | #endif
103 | }
104 |
105 | int mtx_lock(mtx_t *mtx)
106 | {
107 | #if defined(_TTHREAD_WIN32_)
108 | if (!mtx->mTimed)
109 | {
110 | EnterCriticalSection(&(mtx->mHandle.cs));
111 | }
112 | else
113 | {
114 | switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE))
115 | {
116 | case WAIT_OBJECT_0:
117 | break;
118 | case WAIT_ABANDONED:
119 | default:
120 | return thrd_error;
121 | }
122 | }
123 |
124 | if (!mtx->mRecursive)
125 | {
126 | while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
127 | mtx->mAlreadyLocked = TRUE;
128 | }
129 | return thrd_success;
130 | #else
131 | return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
132 | #endif
133 | }
134 |
135 | int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
136 | {
137 | #if defined(_TTHREAD_WIN32_)
138 | struct timespec current_ts;
139 | DWORD timeoutMs;
140 |
141 | if (!mtx->mTimed)
142 | {
143 | return thrd_error;
144 | }
145 |
146 | timespec_get(¤t_ts, TIME_UTC);
147 |
148 | if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec)))
149 | {
150 | timeoutMs = 0;
151 | }
152 | else
153 | {
154 | timeoutMs = (DWORD)(ts->tv_sec - current_ts.tv_sec) * 1000;
155 | timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000;
156 | timeoutMs += 1;
157 | }
158 |
159 | /* TODO: the timeout for WaitForSingleObject doesn't include time
160 | while the computer is asleep. */
161 | switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs))
162 | {
163 | case WAIT_OBJECT_0:
164 | break;
165 | case WAIT_TIMEOUT:
166 | return thrd_timedout;
167 | case WAIT_ABANDONED:
168 | default:
169 | return thrd_error;
170 | }
171 |
172 | if (!mtx->mRecursive)
173 | {
174 | while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
175 | mtx->mAlreadyLocked = TRUE;
176 | }
177 |
178 | return thrd_success;
179 | #elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L)
180 | switch (pthread_mutex_timedlock(mtx, ts)) {
181 | case 0:
182 | return thrd_success;
183 | case ETIMEDOUT:
184 | return thrd_timedout;
185 | default:
186 | return thrd_error;
187 | }
188 | #else
189 | int rc;
190 | struct timespec cur, dur;
191 |
192 | /* Try to acquire the lock and, if we fail, sleep for 5ms. */
193 | while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) {
194 | timespec_get(&cur, TIME_UTC);
195 |
196 | if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec)))
197 | {
198 | break;
199 | }
200 |
201 | dur.tv_sec = ts->tv_sec - cur.tv_sec;
202 | dur.tv_nsec = ts->tv_nsec - cur.tv_nsec;
203 | if (dur.tv_nsec < 0)
204 | {
205 | dur.tv_sec--;
206 | dur.tv_nsec += 1000000000;
207 | }
208 |
209 | if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000))
210 | {
211 | dur.tv_sec = 0;
212 | dur.tv_nsec = 5000000;
213 | }
214 |
215 | nanosleep(&dur, NULL);
216 | }
217 |
218 | switch (rc) {
219 | case 0:
220 | return thrd_success;
221 | case ETIMEDOUT:
222 | case EBUSY:
223 | return thrd_timedout;
224 | default:
225 | return thrd_error;
226 | }
227 | #endif
228 | }
229 |
230 | int mtx_trylock(mtx_t *mtx)
231 | {
232 | #if defined(_TTHREAD_WIN32_)
233 | int ret;
234 |
235 | if (!mtx->mTimed)
236 | {
237 | ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy;
238 | }
239 | else
240 | {
241 | ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy;
242 | }
243 |
244 | if ((!mtx->mRecursive) && (ret == thrd_success))
245 | {
246 | if (mtx->mAlreadyLocked)
247 | {
248 | LeaveCriticalSection(&(mtx->mHandle.cs));
249 | ret = thrd_busy;
250 | }
251 | else
252 | {
253 | mtx->mAlreadyLocked = TRUE;
254 | }
255 | }
256 | return ret;
257 | #else
258 | return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
259 | #endif
260 | }
261 |
262 | int mtx_unlock(mtx_t *mtx)
263 | {
264 | #if defined(_TTHREAD_WIN32_)
265 | mtx->mAlreadyLocked = FALSE;
266 | if (!mtx->mTimed)
267 | {
268 | LeaveCriticalSection(&(mtx->mHandle.cs));
269 | }
270 | else
271 | {
272 | if (!ReleaseMutex(mtx->mHandle.mut))
273 | {
274 | return thrd_error;
275 | }
276 | }
277 | return thrd_success;
278 | #else
279 | return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
280 | #endif
281 | }
282 |
283 | #if defined(_TTHREAD_WIN32_)
284 | #define _CONDITION_EVENT_ONE 0
285 | #define _CONDITION_EVENT_ALL 1
286 | #endif
287 |
288 | int cnd_init(cnd_t *cond)
289 | {
290 | #if defined(_TTHREAD_WIN32_)
291 | cond->mWaitersCount = 0;
292 |
293 | /* Init critical section */
294 | InitializeCriticalSection(&cond->mWaitersCountLock);
295 |
296 | /* Init events */
297 | cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
298 | if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
299 | {
300 | cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
301 | return thrd_error;
302 | }
303 | cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
304 | if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
305 | {
306 | CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
307 | cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
308 | return thrd_error;
309 | }
310 |
311 | return thrd_success;
312 | #else
313 | return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
314 | #endif
315 | }
316 |
317 | void cnd_destroy(cnd_t *cond)
318 | {
319 | #if defined(_TTHREAD_WIN32_)
320 | if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
321 | {
322 | CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
323 | }
324 | if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
325 | {
326 | CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
327 | }
328 | DeleteCriticalSection(&cond->mWaitersCountLock);
329 | #else
330 | pthread_cond_destroy(cond);
331 | #endif
332 | }
333 |
334 | int cnd_signal(cnd_t *cond)
335 | {
336 | #if defined(_TTHREAD_WIN32_)
337 | int haveWaiters;
338 |
339 | /* Are there any waiters? */
340 | EnterCriticalSection(&cond->mWaitersCountLock);
341 | haveWaiters = (cond->mWaitersCount > 0);
342 | LeaveCriticalSection(&cond->mWaitersCountLock);
343 |
344 | /* If we have any waiting threads, send them a signal */
345 | if(haveWaiters)
346 | {
347 | if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
348 | {
349 | return thrd_error;
350 | }
351 | }
352 |
353 | return thrd_success;
354 | #else
355 | return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
356 | #endif
357 | }
358 |
359 | int cnd_broadcast(cnd_t *cond)
360 | {
361 | #if defined(_TTHREAD_WIN32_)
362 | int haveWaiters;
363 |
364 | /* Are there any waiters? */
365 | EnterCriticalSection(&cond->mWaitersCountLock);
366 | haveWaiters = (cond->mWaitersCount > 0);
367 | LeaveCriticalSection(&cond->mWaitersCountLock);
368 |
369 | /* If we have any waiting threads, send them a signal */
370 | if(haveWaiters)
371 | {
372 | if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
373 | {
374 | return thrd_error;
375 | }
376 | }
377 |
378 | return thrd_success;
379 | #else
380 | return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error;
381 | #endif
382 | }
383 |
384 | #if defined(_TTHREAD_WIN32_)
385 | static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
386 | {
387 | DWORD result;
388 | int lastWaiter;
389 |
390 | /* Increment number of waiters */
391 | EnterCriticalSection(&cond->mWaitersCountLock);
392 | ++ cond->mWaitersCount;
393 | LeaveCriticalSection(&cond->mWaitersCountLock);
394 |
395 | /* Release the mutex while waiting for the condition (will decrease
396 | the number of waiters when done)... */
397 | mtx_unlock(mtx);
398 |
399 | /* Wait for either event to become signaled due to cnd_signal() or
400 | cnd_broadcast() being called */
401 | result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
402 | if (result == WAIT_TIMEOUT)
403 | {
404 | /* The mutex is locked again before the function returns, even if an error occurred */
405 | mtx_lock(mtx);
406 | return thrd_timedout;
407 | }
408 | else if (result == WAIT_FAILED)
409 | {
410 | /* The mutex is locked again before the function returns, even if an error occurred */
411 | mtx_lock(mtx);
412 | return thrd_error;
413 | }
414 |
415 | /* Check if we are the last waiter */
416 | EnterCriticalSection(&cond->mWaitersCountLock);
417 | -- cond->mWaitersCount;
418 | lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
419 | (cond->mWaitersCount == 0);
420 | LeaveCriticalSection(&cond->mWaitersCountLock);
421 |
422 | /* If we are the last waiter to be notified to stop waiting, reset the event */
423 | if (lastWaiter)
424 | {
425 | if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
426 | {
427 | /* The mutex is locked again before the function returns, even if an error occurred */
428 | mtx_lock(mtx);
429 | return thrd_error;
430 | }
431 | }
432 |
433 | /* Re-acquire the mutex */
434 | mtx_lock(mtx);
435 |
436 | return thrd_success;
437 | }
438 | #endif
439 |
440 | int cnd_wait(cnd_t *cond, mtx_t *mtx)
441 | {
442 | #if defined(_TTHREAD_WIN32_)
443 | return _cnd_timedwait_win32(cond, mtx, INFINITE);
444 | #else
445 | return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
446 | #endif
447 | }
448 |
449 | int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
450 | {
451 | #if defined(_TTHREAD_WIN32_)
452 | struct timespec now;
453 | if (timespec_get(&now, TIME_UTC) == TIME_UTC)
454 | {
455 | unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000;
456 | unsigned long long tsInMilliseconds = ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
457 | DWORD delta = (tsInMilliseconds > nowInMilliseconds) ?
458 | (DWORD)(tsInMilliseconds - nowInMilliseconds) : 0;
459 | return _cnd_timedwait_win32(cond, mtx, delta);
460 | }
461 | else
462 | return thrd_error;
463 | #else
464 | int ret;
465 | ret = pthread_cond_timedwait(cond, mtx, ts);
466 | if (ret == ETIMEDOUT)
467 | {
468 | return thrd_timedout;
469 | }
470 | return ret == 0 ? thrd_success : thrd_error;
471 | #endif
472 | }
473 |
474 | #if defined(_TTHREAD_WIN32_)
475 | struct TinyCThreadTSSData {
476 | void* value;
477 | tss_t key;
478 | struct TinyCThreadTSSData* next;
479 | };
480 |
481 | static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, };
482 |
483 | static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL;
484 | static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL;
485 |
486 | static void _tinycthread_tss_cleanup (void);
487 |
488 | static void _tinycthread_tss_cleanup (void) {
489 | struct TinyCThreadTSSData* data;
490 | int iteration;
491 | unsigned int again = 1;
492 | void* value;
493 |
494 | for (iteration = 0 ; iteration < TSS_DTOR_ITERATIONS && again > 0 ; iteration++)
495 | {
496 | again = 0;
497 | for (data = _tinycthread_tss_head ; data != NULL ; data = data->next)
498 | {
499 | if (data->value != NULL)
500 | {
501 | value = data->value;
502 | data->value = NULL;
503 |
504 | if (_tinycthread_tss_dtors[data->key] != NULL)
505 | {
506 | again = 1;
507 | _tinycthread_tss_dtors[data->key](value);
508 | }
509 | }
510 | }
511 | }
512 |
513 | while (_tinycthread_tss_head != NULL) {
514 | data = _tinycthread_tss_head->next;
515 | free (_tinycthread_tss_head);
516 | _tinycthread_tss_head = data;
517 | }
518 | _tinycthread_tss_head = NULL;
519 | _tinycthread_tss_tail = NULL;
520 | }
521 |
522 | static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv)
523 | {
524 | (void)h;
525 | (void)pv;
526 |
527 | if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH))
528 | {
529 | _tinycthread_tss_cleanup();
530 | }
531 | }
532 |
533 | #if defined(_MSC_VER)
534 | #ifdef _M_X64
535 | #pragma const_seg(".CRT$XLB")
536 | #else
537 | #pragma data_seg(".CRT$XLB")
538 | #endif
539 | PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback;
540 | #ifdef _M_X64
541 | #pragma data_seg()
542 | #else
543 | #pragma const_seg()
544 | #endif
545 | #else
546 | PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback;
547 | #endif
548 |
549 | #endif /* defined(_TTHREAD_WIN32_) */
550 |
551 | /** Information to pass to the new thread (what to run). */
552 | typedef struct {
553 | thrd_start_t mFunction; /**< Pointer to the function to be executed. */
554 | void * mArg; /**< Function argument for the thread function. */
555 | } _thread_start_info;
556 |
557 | /* Thread wrapper function. */
558 | #if defined(_TTHREAD_WIN32_)
559 | static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg)
560 | #elif defined(_TTHREAD_POSIX_)
561 | static void * _thrd_wrapper_function(void * aArg)
562 | #endif
563 | {
564 | thrd_start_t fun;
565 | void *arg;
566 | int res;
567 |
568 | /* Get thread startup information */
569 | _thread_start_info *ti = (_thread_start_info *) aArg;
570 | fun = ti->mFunction;
571 | arg = ti->mArg;
572 |
573 | /* The thread is responsible for freeing the startup information */
574 | free((void *)ti);
575 |
576 | /* Call the actual client thread function */
577 | res = fun(arg);
578 |
579 | #if defined(_TTHREAD_WIN32_)
580 | if (_tinycthread_tss_head != NULL)
581 | {
582 | _tinycthread_tss_cleanup();
583 | }
584 |
585 | return (DWORD)res;
586 | #else
587 | return (void*)(intptr_t)res;
588 | #endif
589 | }
590 |
591 | int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
592 | {
593 | /* Fill out the thread startup information (passed to the thread wrapper,
594 | which will eventually free it) */
595 | _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
596 | if (ti == NULL)
597 | {
598 | return thrd_nomem;
599 | }
600 | ti->mFunction = func;
601 | ti->mArg = arg;
602 |
603 | /* Create the thread */
604 | #if defined(_TTHREAD_WIN32_)
605 | *thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL);
606 | #elif defined(_TTHREAD_POSIX_)
607 | if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
608 | {
609 | *thr = 0;
610 | }
611 | #endif
612 |
613 | /* Did we fail to create the thread? */
614 | if(!*thr)
615 | {
616 | free(ti);
617 | return thrd_error;
618 | }
619 |
620 | return thrd_success;
621 | }
622 |
623 | thrd_t thrd_current(void)
624 | {
625 | #if defined(_TTHREAD_WIN32_)
626 | return GetCurrentThread();
627 | #else
628 | return pthread_self();
629 | #endif
630 | }
631 |
632 | int thrd_detach(thrd_t thr)
633 | {
634 | #if defined(_TTHREAD_WIN32_)
635 | /* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */
636 | return CloseHandle(thr) != 0 ? thrd_success : thrd_error;
637 | #else
638 | return pthread_detach(thr) == 0 ? thrd_success : thrd_error;
639 | #endif
640 | }
641 |
642 | int thrd_equal(thrd_t thr0, thrd_t thr1)
643 | {
644 | #if defined(_TTHREAD_WIN32_)
645 | return GetThreadId(thr0) == GetThreadId(thr1);
646 | #else
647 | return pthread_equal(thr0, thr1);
648 | #endif
649 | }
650 |
651 | void thrd_exit(int res)
652 | {
653 | #if defined(_TTHREAD_WIN32_)
654 | if (_tinycthread_tss_head != NULL)
655 | {
656 | _tinycthread_tss_cleanup();
657 | }
658 |
659 | ExitThread((DWORD)res);
660 | #else
661 | pthread_exit((void*)(intptr_t)res);
662 | #endif
663 | }
664 |
665 | int thrd_join(thrd_t thr, int *res)
666 | {
667 | #if defined(_TTHREAD_WIN32_)
668 | DWORD dwRes;
669 |
670 | if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
671 | {
672 | return thrd_error;
673 | }
674 | if (res != NULL)
675 | {
676 | if (GetExitCodeThread(thr, &dwRes) != 0)
677 | {
678 | *res = (int) dwRes;
679 | }
680 | else
681 | {
682 | return thrd_error;
683 | }
684 | }
685 | CloseHandle(thr);
686 | #elif defined(_TTHREAD_POSIX_)
687 | void *pres;
688 | if (pthread_join(thr, &pres) != 0)
689 | {
690 | return thrd_error;
691 | }
692 | if (res != NULL)
693 | {
694 | *res = (int)(intptr_t)pres;
695 | }
696 | #endif
697 | return thrd_success;
698 | }
699 |
700 | int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
701 | {
702 | #if !defined(_TTHREAD_WIN32_)
703 | int res = nanosleep(duration, remaining);
704 | if (res == 0) {
705 | return 0;
706 | } else if (errno == EINTR) {
707 | return -1;
708 | } else {
709 | return -2;
710 | }
711 | #else
712 | struct timespec start;
713 | DWORD t;
714 |
715 | timespec_get(&start, TIME_UTC);
716 |
717 | t = SleepEx((DWORD)(duration->tv_sec * 1000 +
718 | duration->tv_nsec / 1000000 +
719 | (((duration->tv_nsec % 1000000) == 0) ? 0 : 1)),
720 | TRUE);
721 |
722 | if (t == 0) {
723 | return 0;
724 | } else {
725 | if (remaining != NULL) {
726 | timespec_get(remaining, TIME_UTC);
727 | remaining->tv_sec -= start.tv_sec;
728 | remaining->tv_nsec -= start.tv_nsec;
729 | if (remaining->tv_nsec < 0)
730 | {
731 | remaining->tv_nsec += 1000000000;
732 | remaining->tv_sec -= 1;
733 | }
734 | }
735 |
736 | return (t == WAIT_IO_COMPLETION) ? -1 : -2;
737 | }
738 | #endif
739 | }
740 |
741 | void thrd_yield(void)
742 | {
743 | #if defined(_TTHREAD_WIN32_)
744 | Sleep(0);
745 | #else
746 | sched_yield();
747 | #endif
748 | }
749 |
750 | int tss_create(tss_t *key, tss_dtor_t dtor)
751 | {
752 | #if defined(_TTHREAD_WIN32_)
753 | *key = TlsAlloc();
754 | if (*key == TLS_OUT_OF_INDEXES)
755 | {
756 | return thrd_error;
757 | }
758 | _tinycthread_tss_dtors[*key] = dtor;
759 | #else
760 | if (pthread_key_create(key, dtor) != 0)
761 | {
762 | return thrd_error;
763 | }
764 | #endif
765 | return thrd_success;
766 | }
767 |
768 | void tss_delete(tss_t key)
769 | {
770 | #if defined(_TTHREAD_WIN32_)
771 | struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key);
772 | struct TinyCThreadTSSData* prev = NULL;
773 | if (data != NULL)
774 | {
775 | if (data == _tinycthread_tss_head)
776 | {
777 | _tinycthread_tss_head = data->next;
778 | }
779 | else
780 | {
781 | prev = _tinycthread_tss_head;
782 | if (prev != NULL)
783 | {
784 | while (prev->next != data)
785 | {
786 | prev = prev->next;
787 | }
788 | }
789 | }
790 |
791 | if (data == _tinycthread_tss_tail)
792 | {
793 | _tinycthread_tss_tail = prev;
794 | }
795 |
796 | free (data);
797 | }
798 | _tinycthread_tss_dtors[key] = NULL;
799 | TlsFree(key);
800 | #else
801 | pthread_key_delete(key);
802 | #endif
803 | }
804 |
805 | void *tss_get(tss_t key)
806 | {
807 | #if defined(_TTHREAD_WIN32_)
808 | struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
809 | if (data == NULL)
810 | {
811 | return NULL;
812 | }
813 | return data->value;
814 | #else
815 | return pthread_getspecific(key);
816 | #endif
817 | }
818 |
819 | int tss_set(tss_t key, void *val)
820 | {
821 | #if defined(_TTHREAD_WIN32_)
822 | struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
823 | if (data == NULL)
824 | {
825 | data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData));
826 | if (data == NULL)
827 | {
828 | return thrd_error;
829 | }
830 |
831 | data->value = NULL;
832 | data->key = key;
833 | data->next = NULL;
834 |
835 | if (_tinycthread_tss_tail != NULL)
836 | {
837 | _tinycthread_tss_tail->next = data;
838 | }
839 | else
840 | {
841 | _tinycthread_tss_tail = data;
842 | }
843 |
844 | if (_tinycthread_tss_head == NULL)
845 | {
846 | _tinycthread_tss_head = data;
847 | }
848 |
849 | if (!TlsSetValue(key, data))
850 | {
851 | free (data);
852 | return thrd_error;
853 | }
854 | }
855 | data->value = val;
856 | #else
857 | if (pthread_setspecific(key, val) != 0)
858 | {
859 | return thrd_error;
860 | }
861 | #endif
862 | return thrd_success;
863 | }
864 |
865 | #if defined(_TTHREAD_EMULATE_TIMESPEC_GET_)
866 | int _tthread_timespec_get(struct timespec *ts, int base)
867 | {
868 | #if defined(_TTHREAD_WIN32_)
869 | struct _timeb tb;
870 | #elif !defined(CLOCK_REALTIME)
871 | struct timeval tv;
872 | #endif
873 |
874 | if (base != TIME_UTC)
875 | {
876 | return 0;
877 | }
878 |
879 | #if defined(_TTHREAD_WIN32_)
880 | _ftime_s(&tb);
881 | ts->tv_sec = (time_t)tb.time;
882 | ts->tv_nsec = 1000000L * (long)tb.millitm;
883 | #elif defined(CLOCK_REALTIME)
884 | base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0;
885 | #else
886 | gettimeofday(&tv, NULL);
887 | ts->tv_sec = (time_t)tv.tv_sec;
888 | ts->tv_nsec = 1000L * (long)tv.tv_usec;
889 | #endif
890 |
891 | return base;
892 | }
893 | #endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */
894 |
895 | #if defined(_TTHREAD_WIN32_)
896 | void call_once(once_flag *flag, void (*func)(void))
897 | {
898 | /* The idea here is that we use a spin lock (via the
899 | InterlockedCompareExchange function) to restrict access to the
900 | critical section until we have initialized it, then we use the
901 | critical section to block until the callback has completed
902 | execution. */
903 | while (flag->status < 3)
904 | {
905 | switch (flag->status)
906 | {
907 | case 0:
908 | if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) {
909 | InitializeCriticalSection(&(flag->lock));
910 | EnterCriticalSection(&(flag->lock));
911 | flag->status = 2;
912 | func();
913 | flag->status = 3;
914 | LeaveCriticalSection(&(flag->lock));
915 | return;
916 | }
917 | break;
918 | case 1:
919 | break;
920 | case 2:
921 | EnterCriticalSection(&(flag->lock));
922 | LeaveCriticalSection(&(flag->lock));
923 | break;
924 | }
925 | }
926 | }
927 | #endif /* defined(_TTHREAD_WIN32_) */
928 |
929 | #ifdef __cplusplus
930 | }
931 | #endif
932 |
--------------------------------------------------------------------------------
/tinycthread.h:
--------------------------------------------------------------------------------
1 | /* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
2 | Copyright (c) 2012 Marcus Geelnard
3 | Copyright (c) 2013-2016 Evan Nemerson
4 |
5 | This software is provided 'as-is', without any express or implied
6 | warranty. In no event will the authors be held liable for any damages
7 | arising from the use of this software.
8 |
9 | Permission is granted to anyone to use this software for any purpose,
10 | including commercial applications, and to alter it and redistribute it
11 | freely, subject to the following restrictions:
12 |
13 | 1. The origin of this software must not be misrepresented; you must not
14 | claim that you wrote the original software. If you use this software
15 | in a product, an acknowledgment in the product documentation would be
16 | appreciated but is not required.
17 |
18 | 2. Altered source versions must be plainly marked as such, and must not be
19 | misrepresented as being the original software.
20 |
21 | 3. This notice may not be removed or altered from any source
22 | distribution.
23 | */
24 |
25 | #ifndef _TINYCTHREAD_H_
26 | #define _TINYCTHREAD_H_
27 |
28 | #ifdef __cplusplus
29 | extern "C" {
30 | #endif
31 |
32 | /**
33 | * @file
34 | * @mainpage TinyCThread API Reference
35 | *
36 | * @section intro_sec Introduction
37 | * TinyCThread is a minimal, portable implementation of basic threading
38 | * classes for C.
39 | *
40 | * They closely mimic the functionality and naming of the C11 standard, and
41 | * should be easily replaceable with the corresponding standard variants.
42 | *
43 | * @section port_sec Portability
44 | * The Win32 variant uses the native Win32 API for implementing the thread
45 | * classes, while for other systems, the POSIX threads API (pthread) is used.
46 | *
47 | * @section misc_sec Miscellaneous
48 | * The following special keywords are available: #_Thread_local.
49 | *
50 | * For more detailed information, browse the different sections of this
51 | * documentation. A good place to start is:
52 | * tinycthread.h.
53 | */
54 |
55 | /* Which platform are we on? */
56 | #if !defined(_TTHREAD_PLATFORM_DEFINED_)
57 | #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
58 | #define _TTHREAD_WIN32_
59 | #else
60 | #define _TTHREAD_POSIX_
61 | #endif
62 | #define _TTHREAD_PLATFORM_DEFINED_
63 | #endif
64 |
65 | /* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
66 | #if defined(_TTHREAD_POSIX_)
67 | #undef _FEATURES_H
68 | #if !defined(_GNU_SOURCE)
69 | #define _GNU_SOURCE
70 | #endif
71 | #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
72 | #undef _POSIX_C_SOURCE
73 | #define _POSIX_C_SOURCE 199309L
74 | #endif
75 | #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
76 | #undef _XOPEN_SOURCE
77 | #define _XOPEN_SOURCE 500
78 | #endif
79 | #define _XPG6
80 | #endif
81 |
82 | /* Generic includes */
83 | #include
84 |
85 | /* Platform specific includes */
86 | #if defined(_TTHREAD_POSIX_)
87 | #include
88 | #elif defined(_TTHREAD_WIN32_)
89 | #ifndef WIN32_LEAN_AND_MEAN
90 | #define WIN32_LEAN_AND_MEAN
91 | #define __UNDEF_LEAN_AND_MEAN
92 | #endif
93 | #include
94 | #ifdef __UNDEF_LEAN_AND_MEAN
95 | #undef WIN32_LEAN_AND_MEAN
96 | #undef __UNDEF_LEAN_AND_MEAN
97 | #endif
98 | #endif
99 |
100 | /* Compiler-specific information */
101 | #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
102 | #define TTHREAD_NORETURN _Noreturn
103 | #elif defined(__GNUC__)
104 | #define TTHREAD_NORETURN __attribute__((__noreturn__))
105 | #else
106 | #define TTHREAD_NORETURN
107 | #endif
108 |
109 | /* If TIME_UTC is missing, provide it and provide a wrapper for
110 | timespec_get. */
111 | #ifndef TIME_UTC
112 | #define TIME_UTC 1
113 | #define _TTHREAD_EMULATE_TIMESPEC_GET_
114 |
115 | #if defined(_TTHREAD_WIN32_)
116 | struct _tthread_timespec {
117 | time_t tv_sec;
118 | long tv_nsec;
119 | };
120 | #define timespec _tthread_timespec
121 | #endif
122 |
123 | int _tthread_timespec_get(struct timespec *ts, int base);
124 | #define timespec_get _tthread_timespec_get
125 | #endif
126 |
127 | /** TinyCThread version (major number). */
128 | #define TINYCTHREAD_VERSION_MAJOR 1
129 | /** TinyCThread version (minor number). */
130 | #define TINYCTHREAD_VERSION_MINOR 2
131 | /** TinyCThread version (full version). */
132 | #define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
133 |
134 | /**
135 | * @def _Thread_local
136 | * Thread local storage keyword.
137 | * A variable that is declared with the @c _Thread_local keyword makes the
138 | * value of the variable local to each thread (known as thread-local storage,
139 | * or TLS). Example usage:
140 | * @code
141 | * // This variable is local to each thread.
142 | * _Thread_local int variable;
143 | * @endcode
144 | * @note The @c _Thread_local keyword is a macro that maps to the corresponding
145 | * compiler directive (e.g. @c __declspec(thread)).
146 | * @note This directive is currently not supported on Mac OS X (it will give
147 | * a compiler error), since compile-time TLS is not supported in the Mac OS X
148 | * executable format. Also, some older versions of MinGW (before GCC 4.x) do
149 | * not support this directive, nor does the Tiny C Compiler.
150 | * @hideinitializer
151 | */
152 |
153 | #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
154 | #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
155 | #define _Thread_local __thread
156 | #else
157 | #define _Thread_local __declspec(thread)
158 | #endif
159 | #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9))
160 | #define _Thread_local __thread
161 | #endif
162 |
163 | /* Macros */
164 | #if defined(_TTHREAD_WIN32_)
165 | #define TSS_DTOR_ITERATIONS (4)
166 | #else
167 | #define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
168 | #endif
169 |
170 | /* Function return values */
171 | #define thrd_error 0 /**< The requested operation failed */
172 | #define thrd_success 1 /**< The requested operation succeeded */
173 | #define thrd_timedout 2 /**< The time specified in the call was reached without acquiring the requested resource */
174 | #define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */
175 | #define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */
176 |
177 | /* Mutex types */
178 | #define mtx_plain 0
179 | #define mtx_timed 1
180 | #define mtx_recursive 2
181 |
182 | /* Mutex */
183 | #if defined(_TTHREAD_WIN32_)
184 | typedef struct {
185 | union {
186 | CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */
187 | HANDLE mut; /* Mutex handle (used for timed mutex) */
188 | } mHandle; /* Mutex handle */
189 | int mAlreadyLocked; /* TRUE if the mutex is already locked */
190 | int mRecursive; /* TRUE if the mutex is recursive */
191 | int mTimed; /* TRUE if the mutex is timed */
192 | } mtx_t;
193 | #else
194 | typedef pthread_mutex_t mtx_t;
195 | #endif
196 |
197 | /** Create a mutex object.
198 | * @param mtx A mutex object.
199 | * @param type Bit-mask that must have one of the following six values:
200 | * @li @c mtx_plain for a simple non-recursive mutex
201 | * @li @c mtx_timed for a non-recursive mutex that supports timeout
202 | * @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
203 | * @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
204 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
205 | * not be honored.
206 | */
207 | int mtx_init(mtx_t *mtx, int type);
208 |
209 | /** Release any resources used by the given mutex.
210 | * @param mtx A mutex object.
211 | */
212 | void mtx_destroy(mtx_t *mtx);
213 |
214 | /** Lock the given mutex.
215 | * Blocks until the given mutex can be locked. If the mutex is non-recursive, and
216 | * the calling thread already has a lock on the mutex, this call will block
217 | * forever.
218 | * @param mtx A mutex object.
219 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
220 | * not be honored.
221 | */
222 | int mtx_lock(mtx_t *mtx);
223 |
224 | /** Lock the given mutex, or block until a specific point in time.
225 | * Blocks until either the given mutex can be locked, or the specified TIME_UTC
226 | * based time.
227 | * @param mtx A mutex object.
228 | * @param ts A UTC based calendar time
229 | * @return @ref The mtx_timedlock function returns thrd_success on success, or
230 | * thrd_timedout if the time specified was reached without acquiring the
231 | * requested resource, or thrd_error if the request could not be honored.
232 | */
233 | int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
234 |
235 | /** Try to lock the given mutex.
236 | * The specified mutex shall support either test and return or timeout. If the
237 | * mutex is already locked, the function returns without blocking.
238 | * @param mtx A mutex object.
239 | * @return @ref thrd_success on success, or @ref thrd_busy if the resource
240 | * requested is already in use, or @ref thrd_error if the request could not be
241 | * honored.
242 | */
243 | int mtx_trylock(mtx_t *mtx);
244 |
245 | /** Unlock the given mutex.
246 | * @param mtx A mutex object.
247 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
248 | * not be honored.
249 | */
250 | int mtx_unlock(mtx_t *mtx);
251 |
252 | /* Condition variable */
253 | #if defined(_TTHREAD_WIN32_)
254 | typedef struct {
255 | HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */
256 | unsigned int mWaitersCount; /* Count of the number of waiters. */
257 | CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
258 | } cnd_t;
259 | #else
260 | typedef pthread_cond_t cnd_t;
261 | #endif
262 |
263 | /** Create a condition variable object.
264 | * @param cond A condition variable object.
265 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
266 | * not be honored.
267 | */
268 | int cnd_init(cnd_t *cond);
269 |
270 | /** Release any resources used by the given condition variable.
271 | * @param cond A condition variable object.
272 | */
273 | void cnd_destroy(cnd_t *cond);
274 |
275 | /** Signal a condition variable.
276 | * Unblocks one of the threads that are blocked on the given condition variable
277 | * at the time of the call. If no threads are blocked on the condition variable
278 | * at the time of the call, the function does nothing and return success.
279 | * @param cond A condition variable object.
280 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
281 | * not be honored.
282 | */
283 | int cnd_signal(cnd_t *cond);
284 |
285 | /** Broadcast a condition variable.
286 | * Unblocks all of the threads that are blocked on the given condition variable
287 | * at the time of the call. If no threads are blocked on the condition variable
288 | * at the time of the call, the function does nothing and return success.
289 | * @param cond A condition variable object.
290 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
291 | * not be honored.
292 | */
293 | int cnd_broadcast(cnd_t *cond);
294 |
295 | /** Wait for a condition variable to become signaled.
296 | * The function atomically unlocks the given mutex and endeavors to block until
297 | * the given condition variable is signaled by a call to cnd_signal or to
298 | * cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
299 | * before it returns.
300 | * @param cond A condition variable object.
301 | * @param mtx A mutex object.
302 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
303 | * not be honored.
304 | */
305 | int cnd_wait(cnd_t *cond, mtx_t *mtx);
306 |
307 | /** Wait for a condition variable to become signaled.
308 | * The function atomically unlocks the given mutex and endeavors to block until
309 | * the given condition variable is signaled by a call to cnd_signal or to
310 | * cnd_broadcast, or until after the specified time. When the calling thread
311 | * becomes unblocked it locks the mutex before it returns.
312 | * @param cond A condition variable object.
313 | * @param mtx A mutex object.
314 | * @param xt A point in time at which the request will time out (absolute time).
315 | * @return @ref thrd_success upon success, or @ref thrd_timeout if the time
316 | * specified in the call was reached without acquiring the requested resource, or
317 | * @ref thrd_error if the request could not be honored.
318 | */
319 | int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
320 |
321 | /* Thread */
322 | #if defined(_TTHREAD_WIN32_)
323 | typedef HANDLE thrd_t;
324 | #else
325 | typedef pthread_t thrd_t;
326 | #endif
327 |
328 | /** Thread start function.
329 | * Any thread that is started with the @ref thrd_create() function must be
330 | * started through a function of this type.
331 | * @param arg The thread argument (the @c arg argument of the corresponding
332 | * @ref thrd_create() call).
333 | * @return The thread return value, which can be obtained by another thread
334 | * by using the @ref thrd_join() function.
335 | */
336 | typedef int (*thrd_start_t)(void *arg);
337 |
338 | /** Create a new thread.
339 | * @param thr Identifier of the newly created thread.
340 | * @param func A function pointer to the function that will be executed in
341 | * the new thread.
342 | * @param arg An argument to the thread function.
343 | * @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
344 | * be allocated for the thread requested, or @ref thrd_error if the request
345 | * could not be honored.
346 | * @note A thread’s identifier may be reused for a different thread once the
347 | * original thread has exited and either been detached or joined to another
348 | * thread.
349 | */
350 | int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
351 |
352 | /** Identify the calling thread.
353 | * @return The identifier of the calling thread.
354 | */
355 | thrd_t thrd_current(void);
356 |
357 | /** Dispose of any resources allocated to the thread when that thread exits.
358 | * @return thrd_success, or thrd_error on error
359 | */
360 | int thrd_detach(thrd_t thr);
361 |
362 | /** Compare two thread identifiers.
363 | * The function determines if two thread identifiers refer to the same thread.
364 | * @return Zero if the two thread identifiers refer to different threads.
365 | * Otherwise a nonzero value is returned.
366 | */
367 | int thrd_equal(thrd_t thr0, thrd_t thr1);
368 |
369 | /** Terminate execution of the calling thread.
370 | * @param res Result code of the calling thread.
371 | */
372 | TTHREAD_NORETURN void thrd_exit(int res);
373 |
374 | /** Wait for a thread to terminate.
375 | * The function joins the given thread with the current thread by blocking
376 | * until the other thread has terminated.
377 | * @param thr The thread to join with.
378 | * @param res If this pointer is not NULL, the function will store the result
379 | * code of the given thread in the integer pointed to by @c res.
380 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
381 | * not be honored.
382 | */
383 | int thrd_join(thrd_t thr, int *res);
384 |
385 | /** Put the calling thread to sleep.
386 | * Suspend execution of the calling thread.
387 | * @param duration Interval to sleep for
388 | * @param remaining If non-NULL, this parameter will hold the remaining
389 | * time until time_point upon return. This will
390 | * typically be zero, but if the thread was woken up
391 | * by a signal that is not ignored before duration was
392 | * reached @c remaining will hold a positive time.
393 | * @return 0 (zero) on successful sleep, -1 if an interrupt occurred,
394 | * or a negative value if the operation fails.
395 | */
396 | int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
397 |
398 | /** Yield execution to another thread.
399 | * Permit other threads to run, even if the current thread would ordinarily
400 | * continue to run.
401 | */
402 | void thrd_yield(void);
403 |
404 | /* Thread local storage */
405 | #if defined(_TTHREAD_WIN32_)
406 | typedef DWORD tss_t;
407 | #else
408 | typedef pthread_key_t tss_t;
409 | #endif
410 |
411 | /** Destructor function for a thread-specific storage.
412 | * @param val The value of the destructed thread-specific storage.
413 | */
414 | typedef void (*tss_dtor_t)(void *val);
415 |
416 | /** Create a thread-specific storage.
417 | * @param key The unique key identifier that will be set if the function is
418 | * successful.
419 | * @param dtor Destructor function. This can be NULL.
420 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
421 | * not be honored.
422 | * @note On Windows, the @c dtor will definitely be called when
423 | * appropriate for threads created with @ref thrd_create. It will be
424 | * called for other threads in most cases, the possible exception being
425 | * for DLLs loaded with LoadLibraryEx. In order to be certain, you
426 | * should use @ref thrd_create whenever possible.
427 | */
428 | int tss_create(tss_t *key, tss_dtor_t dtor);
429 |
430 | /** Delete a thread-specific storage.
431 | * The function releases any resources used by the given thread-specific
432 | * storage.
433 | * @param key The key that shall be deleted.
434 | */
435 | void tss_delete(tss_t key);
436 |
437 | /** Get the value for a thread-specific storage.
438 | * @param key The thread-specific storage identifier.
439 | * @return The value for the current thread held in the given thread-specific
440 | * storage.
441 | */
442 | void *tss_get(tss_t key);
443 |
444 | /** Set the value for a thread-specific storage.
445 | * @param key The thread-specific storage identifier.
446 | * @param val The value of the thread-specific storage to set for the current
447 | * thread.
448 | * @return @ref thrd_success on success, or @ref thrd_error if the request could
449 | * not be honored.
450 | */
451 | int tss_set(tss_t key, void *val);
452 |
453 | #if defined(_TTHREAD_WIN32_)
454 | typedef struct {
455 | LONG volatile status;
456 | CRITICAL_SECTION lock;
457 | } once_flag;
458 | #define ONCE_FLAG_INIT {0,}
459 | #else
460 | #define once_flag pthread_once_t
461 | #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
462 | #endif
463 |
464 | /** Invoke a callback exactly once
465 | * @param flag Flag used to ensure the callback is invoked exactly
466 | * once.
467 | * @param func Callback to invoke.
468 | */
469 | #if defined(_TTHREAD_WIN32_)
470 | void call_once(once_flag *flag, void (*func)(void));
471 | #else
472 | #define call_once(flag,func) pthread_once(flag,func)
473 | #endif
474 |
475 | #ifdef __cplusplus
476 | }
477 | #endif
478 |
479 | #endif /* _TINYTHREAD_H_ */
480 |
--------------------------------------------------------------------------------
/url.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanghaimoon888/mod_vadasr/900752f40bcd8e182e6967566c061547a1be31a4/url.cpp
--------------------------------------------------------------------------------
/url.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanghaimoon888/mod_vadasr/900752f40bcd8e182e6967566c061547a1be31a4/url.h
--------------------------------------------------------------------------------
/xfasr.cpp:
--------------------------------------------------------------------------------
1 | #include "xfasr.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "md5.h"
9 | #include "url.h"
10 | #include "base64.h"
11 | #include "tinycthread.h"
12 |
13 |
14 |
15 | int ws_thread(void * arg)
16 | {
17 | asr_session_t* asr = (asr_session_t*)arg;
18 |
19 | while (asr->ws->getReadyState() != WebSocket::CLOSED) {
20 | asr->ws->poll();
21 | asr->ws->dispatch(asr->handle_message);
22 | }
23 |
24 | asr->handle_event(asr->asr_text, asr->event_arg);
25 | deinit_asr(asr);
26 |
27 |
28 | }
29 |
30 | WebSocket::pointer init_asr(char* appid, char* key, asr_session_t* asr)
31 | {
32 | WebSocket::pointer ws = NULL;
33 | char tempStr[200];
34 | unsigned char digest[EVP_MAX_MD_SIZE] = { '\0' };
35 | unsigned int digest_len = 0;
36 | char hex[36];
37 | char url[200];
38 | char outdata[200] = { 0 };
39 | int outlen;
40 |
41 | int64_t timeStamp = time(NULL);
42 | std::sprintf(tempStr, "%s%d", appid, timeStamp);
43 | md5((unsigned char*)tempStr, hex);
44 | HMAC(EVP_sha1(), key, strlen(key), (unsigned char*)hex, strlen(hex), digest, &digest_len);
45 |
46 | base64_encode_block((char*)digest, digest_len, (char*)outdata, &outlen);
47 | urlencode(outdata);
48 | std::sprintf(url, "ws://rtasr.xfyun.cn/v1/ws?appid=%s&ts=%d&signa=%s", appid, timeStamp, outdata);
49 | std::string wsUrl = url;
50 | ws = WebSocket::from_url(wsUrl, "", asr);
51 | if (ws)
52 | {
53 | asr->ws = ws;
54 | asr->asr_text = new char[BFLEN]{0};
55 | memset(asr->asr_text, 0, sizeof(asr->asr_text));
56 | if (thrd_create(&asr->thr, ws_thread, (void*)asr) == thrd_success){}
57 | mtx_init(&asr->mutex, mtx_plain);
58 | }
59 |
60 | return ws;
61 | }
62 |
63 | int send_data(asr_session_t* asr, char* buf, int buflen)
64 | {
65 | mtx_lock(&asr->mutex);
66 | if (!asr->ws || buf==NULL)
67 | {
68 | mtx_unlock(&asr->mutex);
69 | return -1;
70 | }
71 |
72 | std::vector data(buf, buf + buflen);
73 |
74 | if ((asr->ws->getReadyState() != WebSocket::CLOSED))
75 | {
76 | asr->ws->sendBinary(data);
77 | }
78 | else
79 | {
80 | mtx_unlock(&asr->mutex);
81 | return -1;
82 | }
83 |
84 | mtx_unlock(&asr->mutex);
85 | return buflen;
86 | }
87 |
88 | int send_end(asr_session_t* asr)
89 | {
90 | char *endbuf = "{\"end\": true}";
91 |
92 | return send_data(asr, endbuf, strlen(endbuf));
93 | }
94 |
95 | void deinit_asr(asr_session_t* asr)
96 | {
97 | try {
98 | mtx_lock(&asr->mutex);
99 | asr->ws->close();
100 | delete asr->ws;
101 | asr->ws = nullptr;
102 | delete asr->asr_text;
103 | mtx_unlock(&asr->mutex);
104 | }
105 | catch(...)
106 | {
107 | printf("get exception\n");
108 | }
109 |
110 | }
--------------------------------------------------------------------------------
/xfasr.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shanghaimoon888/mod_vadasr/900752f40bcd8e182e6967566c061547a1be31a4/xfasr.h
--------------------------------------------------------------------------------