├── 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 --------------------------------------------------------------------------------