1 /*---------------------------------------------------------------------------* 2 * EventLogImpl.c * 3 * * 4 * Copyright 2007, 2008 Nuance Communciations, Inc. * 5 * * 6 * Licensed under the Apache License, Version 2.0 (the 'License'); * 7 * you may not use this file except in compliance with the License. * 8 * * 9 * You may obtain a copy of the License at * 10 * http://www.apache.org/licenses/LICENSE-2.0 * 11 * * 12 * Unless required by applicable law or agreed to in writing, software * 13 * distributed under the License is distributed on an 'AS IS' BASIS, * 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 15 * See the License for the specific language governing permissions and * 16 * limitations under the License. * 17 * * 18 *---------------------------------------------------------------------------*/ 19 20 #ifdef ANDROID 21 #include <sys/time.h> 22 #endif 23 24 #include "errno.h" 25 #include "ESR_Session.h" 26 #include "ESR_SessionType.h" 27 #include "IntArrayList.h" 28 #include "LCHAR.h" 29 #include "PFileSystem.h" 30 #include "SR_EventLog.h" 31 #include "SR_EventLogImpl.h" 32 #include "SR_Session.h" 33 #include "plog.h" 34 #include "pmemory.h" 35 #include "ptimestamp.h" 36 #include "riff.h" 37 #include "pstdio.h" 38 39 #define MTAG NULL 40 41 #define localtime_r(clock, result) ((result)->tm_sec = 0, localtime(clock)) 42 43 44 /*********************************************************************/ 45 /* move this to portable lib */ 46 #ifdef _WIN32 47 #include <windows.h> 48 #include <direct.h> 49 #else 50 #include <time.h> /* For CLK_TCK / CLOCKS_PER_SEC */ 51 #include <sys/times.h> /* for times() */ 52 #ifndef CLK_TCK 53 #define CLK_TCK CLOCKS_PER_SEC 54 #endif 55 #endif 56 57 /** 58 * 59 * @param userTime milliseconds spent in user mode 60 * @param kernelTime milliseconds spent in kernel mode 61 */ 62 ESR_ReturnCode PGetCPUTimes(long* userTime, long* kernelTime) 63 { 64 #ifdef _WIN32 65 FILETIME dummy; 66 FILETIME k, u; 67 LARGE_INTEGER lk, lu; 68 69 if ((! userTime) || (! kernelTime)) 70 return -1; 71 72 if (GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &k, &u) == ESR_FALSE) 73 return -1; 74 75 lk.LowPart = k.dwLowDateTime; 76 lk.HighPart = k.dwHighDateTime; 77 *kernelTime = (long)(lk.QuadPart / 10000); 78 79 lu.LowPart = u.dwLowDateTime; 80 lu.HighPart = u.dwHighDateTime; 81 *userTime = (long)(lu.QuadPart / 10000); 82 83 84 #elif !defined(__vxworks) 85 struct tms timeBuf; 86 87 if ((! userTime) || (! kernelTime)) 88 return -1; 89 90 times(&timeBuf); 91 *userTime = (long)timeBuf.tms_utime * 1000 / CLK_TCK; 92 *kernelTime = (long)timeBuf.tms_stime * 1000 / CLK_TCK; 93 #endif 94 return 0; 95 } 96 /*********************************************************************/ 97 98 ESR_ReturnCode propertyChanged(ESR_SessionTypeListener* self, const LCHAR* name, const void* oldValue, const void* newValue, VariableTypes variableType, void* data) 99 { 100 SR_EventLog* eventLog = (SR_EventLog*) data; 101 IntArrayList* list; 102 size_t len, i, lValueSize = 10; 103 int iValue; 104 LCHAR lValue[10]; 105 ESR_ReturnCode rc; 106 107 switch (variableType) 108 { 109 case TYPES_INT: 110 CHKLOG(rc, SR_EventLogTokenInt(eventLog, name, *((int*) newValue))); 111 break; 112 case TYPES_UINT16_T: 113 CHKLOG(rc, SR_EventLogTokenUint16_t(eventLog, name, *((asr_uint16_t*) newValue))); 114 break; 115 case TYPES_SIZE_T: 116 CHKLOG(rc, SR_EventLogTokenSize_t(eventLog, name, *((size_t*) newValue))); 117 break; 118 case TYPES_BOOL: 119 CHKLOG(rc, SR_EventLogTokenBool(eventLog, name, *((ESR_BOOL*) newValue))); 120 break; 121 case TYPES_FLOAT: 122 CHKLOG(rc, SR_EventLogTokenFloat(eventLog, name, *((float*) newValue))); 123 break; 124 case TYPES_PLCHAR: 125 CHKLOG(rc, SR_EventLogToken(eventLog, name, (LCHAR*) newValue)); 126 break; 127 128 case TYPES_INTARRAYLIST: 129 CHKLOG(rc, ESR_SessionGetProperty(name, (void **)&list, TYPES_INTARRAYLIST)); 130 CHKLOG(rc, list->getSize(list, &len)); 131 CHKLOG(rc, SR_EventLogTokenInt(eventLog, name, len)); 132 for (i = 0; i < len; ++i) 133 { 134 CHKLOG(rc, list->get(list, i, &iValue)); 135 lValueSize = sizeof(lValue); 136 CHKLOG(rc, litostr(i, lValue, &lValueSize, 10)); 137 CHKLOG(rc, SR_EventLogTokenInt(eventLog, lValue, iValue)); 138 } 139 break; 140 141 default: 142 /* do nothing */ 143 ; 144 } 145 return ESR_SUCCESS; 146 CLEANUP: 147 return rc; 148 } 149 150 /** 151 * Implementation copied from original Ian Fox implementation of ALTsleeLog 152 */ 153 154 ESR_ReturnCode SR_EventLogCreate(SR_EventLog** self) 155 { 156 SR_EventLogImpl *impl, *any_existing_eventlog; 157 ESR_ReturnCode rc; 158 LCHAR* dataCaptureDir; 159 #define TIMESTAMP_LENGTH 18 160 LCHAR timeStr[TIMESTAMP_LENGTH]; 161 struct tm *ct, ct_r; 162 PTimeStamp timestamp; 163 #ifdef ANDROID 164 struct timeval dir_stamp; 165 #endif 166 167 if (self == NULL) 168 { 169 PLogError(L("ESR_INVALID_ARGUMENT")); 170 return ESR_INVALID_ARGUMENT; 171 } 172 173 any_existing_eventlog = NULL; 174 rc = ESR_SessionGetProperty(L("eventlog"), (void **)&any_existing_eventlog, TYPES_SR_EVENTLOG); 175 if (rc == ESR_SUCCESS && any_existing_eventlog) 176 { 177 *self = (SR_EventLog*)any_existing_eventlog; 178 PLogError("eventlog was already created"); 179 return ESR_SUCCESS; 180 } 181 182 impl = NEW(SR_EventLogImpl, MTAG); 183 if (impl == NULL) 184 { 185 PLogError(L("ESR_OUT_OF_MEMORY")); 186 return ESR_OUT_OF_MEMORY; 187 } 188 189 impl->Interface.destroy = &SR_EventLog_Destroy; 190 impl->Interface.event = &SR_EventLog_Event; 191 impl->Interface.token = &SR_EventLog_Token; 192 impl->Interface.tokenInt = &SR_EventLog_TokenInt; 193 impl->Interface.tokenUint16_t = &SR_EventLog_TokenUint16_t; 194 impl->Interface.tokenSize_t = &SR_EventLog_TokenSize_t; 195 impl->Interface.tokenBool = &SR_EventLog_TokenBool; 196 impl->Interface.tokenFloat = &SR_EventLog_TokenFloat; 197 impl->Interface.eventSession = &SR_EventLogEventSessionImpl; 198 impl->Interface.audioOpen = &SR_EventLog_AudioOpen; 199 impl->Interface.audioClose = &SR_EventLog_AudioClose; 200 impl->Interface.audioWrite = &SR_EventLog_AudioWrite; 201 impl->Interface.audioGetFilename = &SR_EventLog_AudioGetFilename; 202 impl->sessionListenerPair.data = NULL; 203 impl->sessionListenerPair.listener = &impl->sessionListener; 204 impl->sessionListener.propertyChanged = &propertyChanged; 205 impl->waveformCounter = 0; 206 impl->logFile = NULL; 207 impl->tokenBuf[0] = 0; 208 impl->logFile_state = NO_FILE; 209 impl->logLevel = 0; 210 impl->waveformFile = NULL; 211 LSTRCPY(impl->logFilename, L("")); 212 213 CHKLOG(rc, ESR_SessionSetProperty(L("eventlog"), impl, TYPES_SR_EVENTLOG)); 214 rc = ESR_SessionGetSize_t(L("SREC.Recognizer.osi_log_level"), &impl->logLevel); 215 if (rc == ESR_NO_MATCH_ERROR) 216 { 217 impl->logLevel = 7; 218 CHKLOG(rc, ESR_SessionSetSize_t(L("SREC.Recognizer.osi_log_level"), impl->logLevel)); 219 } 220 else if (rc != ESR_SUCCESS) 221 { 222 PLogError(ESR_rc2str(rc)); 223 goto CLEANUP; 224 } 225 226 if (impl->logLevel > 0) 227 { 228 CHKLOG(rc, ESR_SessionGetProperty(L("cmdline.DataCaptureDirectory"), (void**) &dataCaptureDir, TYPES_PLCHAR)); 229 230 LSTRCPY(impl->logFilename, dataCaptureDir); 231 #ifdef ANDROID 232 /* 233 * The existing functions did not work on the desired platform, hence this code for the device. 234 */ 235 gettimeofday ( &dir_stamp, NULL ); 236 sprintf(timeStr, "%lu", (unsigned long) dir_stamp.tv_sec ); 237 #else 238 PTimeStampSet(×tamp); 239 ct = localtime_r(×tamp.secs, &ct_r); 240 sprintf(timeStr, "%04d%02d%02d%02d%02d%02d", 241 ct->tm_year + 1900, ct->tm_mon + 1, ct->tm_mday, ct->tm_hour, 242 ct->tm_min, ct->tm_sec); 243 #endif 244 /* create capture directory if it doesn't already exist */ 245 rc = pf_make_dir (impl->logFilename); 246 if (rc != ESR_SUCCESS && rc != ESR_IDENTIFIER_COLLISION) 247 { 248 PLogError(ESR_rc2str(rc)); 249 goto CLEANUP; 250 } 251 252 /* create the directory for today's log if it doesn't already exist */ 253 LSTRCAT(impl->logFilename, L("/")); 254 LSTRCAT(impl->logFilename, timeStr); 255 /* 256 * There used to be a while forever loop here with a break, but that caused an infinite loop 257 * for the customer. With 1 second resolution, a pre-existing directory probably means a bug. 258 * It's not worth trying to handle this. 259 */ 260 rc = pf_make_dir (impl->logFilename); 261 if (rc != ESR_SUCCESS) 262 { 263 PLogError(ESR_rc2str(rc)); 264 goto CLEANUP; 265 } 266 267 /* create the log file */ 268 LSTRCAT(impl->logFilename, L("/SWIevent-")); 269 LSTRCAT(impl->logFilename, timeStr); 270 LSTRCAT(impl->logFilename, L(".log")); 271 272 impl->logFile = pfopen ( impl->logFilename, L("w") ); 273 /* CHKLOG(rc, PFileSystemCreatePFile(impl->logFilename, ESR_TRUE, &impl->logFile)); 274 CHKLOG(rc, PFileOpen(impl->logFile, L("w")));*/ 275 276 if ( impl->logFile != NULL ) 277 impl->logFile_state = FILE_OK; 278 else 279 goto CLEANUP; 280 } 281 282 *self = (SR_EventLog*) impl; 283 return ESR_SUCCESS; 284 CLEANUP: 285 if (impl->logFile) 286 pfclose (impl->logFile); 287 return rc; 288 } 289 290 ESR_ReturnCode SR_EventLog_Destroy(SR_EventLog* self) 291 { 292 SR_EventLogImpl* impl = (SR_EventLogImpl*) self; 293 ESR_ReturnCode rc; 294 295 if (impl->logFile_state == FILE_OK) 296 { 297 pfflush(impl->logFile); 298 299 pfclose(impl->logFile); 300 impl->logFile = NULL; 301 impl->logFile_state = NO_FILE; 302 } 303 CHKLOG(rc, ESR_SessionRemoveProperty(L("eventlog"))); 304 FREE(impl); 305 return ESR_SUCCESS; 306 CLEANUP: 307 return rc; 308 } 309 310 311 static int quote_delimiter(LCHAR *record, size_t len) 312 { 313 LCHAR qrecord[TOK_BUFLEN * 2]; 314 LCHAR *s, *d; 315 316 s = record; 317 d = qrecord; 318 while (*s) 319 { 320 if (*s == '|') 321 *d++ = '|'; 322 *d++ = *s++; 323 } 324 *d = L('\0'); 325 326 if (LSTRLEN(qrecord) >= len) 327 return -1; 328 329 LSTRCPY(record, qrecord); 330 331 return 0; 332 } 333 334 335 ESR_ReturnCode SR_EventLog_Token(SR_EventLog* self, const LCHAR* token, const LCHAR *value) 336 { 337 SR_EventLogImpl *impl = (SR_EventLogImpl *)self; 338 LCHAR buf[TOK_BUFLEN]; 339 340 if (self == NULL || token == NULL || value == NULL) 341 return ESR_INVALID_ARGUMENT; 342 if (impl->logLevel == 0) 343 return ESR_SUCCESS; 344 345 /* token cannot contain '=' */ 346 if (LSTRCHR(token, L('=')) != NULL) 347 { 348 PLogError(L("SLEE: Token '%s' contains illegal '=' character"), token); 349 return ESR_INVALID_ARGUMENT; 350 } 351 /* value cannot contain newline */ 352 if (value && LSTRCHR(value, L('\n')) != NULL) 353 { 354 PLogError(L("SLEE: Value for token '%s' contains illegal newline character"), token); 355 return ESR_INVALID_ARGUMENT; 356 } 357 358 /* the number 2 in this if statement refers to the '=' and the '|'. */ 359 if (LSTRLEN(token) + LSTRLEN(value) + 2 + 360 LSTRLEN(impl->tokenBuf) < MAX_LOG_RECORD) 361 { 362 if (LSTRLEN(token) + LSTRLEN(value) + 3 > TOK_BUFLEN) 363 { 364 PLogError(L("ESR_BUFFER_OVERFLOW: SLEE '|%s=%s'"), token, value); 365 return ESR_BUFFER_OVERFLOW; 366 } 367 sprintf(buf, "%s=%s", token, value); 368 if (quote_delimiter(buf, TOK_BUFLEN - 2) != 0) 369 { 370 PLogError(L("ESR_BUFFER_OVERFLOW: SLEE '|%s'"), buf); 371 return ESR_BUFFER_OVERFLOW; 372 } 373 if (LSTRLEN(buf) + 1 + LSTRLEN(impl->tokenBuf) >= MAX_LOG_RECORD) 374 { 375 PLogError(L("ESR_BUFFER_OVERFLOW: SLEE '|%s'"), buf); 376 return ESR_BUFFER_OVERFLOW; 377 } 378 strcat(impl->tokenBuf, "|"); 379 strcat(impl->tokenBuf, buf); 380 } 381 else 382 { 383 PLogError(L("ESR_BUFFER_OVERFLOW: SLEE '|%s=%s'"), token, value); 384 return ESR_BUFFER_OVERFLOW; 385 } 386 return ESR_SUCCESS; 387 } 388 389 ESR_ReturnCode SR_EventLog_TokenInt(SR_EventLog* self, const LCHAR* token, int value) 390 { 391 SR_EventLogImpl *impl = (SR_EventLogImpl *)self; 392 ESR_ReturnCode rc; 393 LCHAR alpha[MAX_INT_DIGITS+1]; 394 size_t size = MAX_INT_DIGITS+1; 395 396 if (impl->logLevel == 0) 397 return ESR_SUCCESS; 398 CHK(rc, litostr(value, alpha, &size, 10)); 399 return self->token(self, token, alpha); 400 CLEANUP: 401 return rc; 402 } 403 404 ESR_ReturnCode SR_EventLog_TokenUint16_t(SR_EventLog* self, const LCHAR* token, asr_uint16_t value) 405 { 406 SR_EventLogImpl *impl = (SR_EventLogImpl *)self; 407 ESR_ReturnCode rc; 408 LCHAR alpha[MAX_INT_DIGITS+1]; 409 size_t size = MAX_INT_DIGITS+1; 410 411 if (impl->logLevel == 0) 412 return ESR_SUCCESS; 413 CHK(rc, lultostr(value, alpha, &size, 10)); 414 return self->token(self, token, alpha); 415 CLEANUP: 416 return rc; 417 } 418 419 ESR_ReturnCode SR_EventLog_TokenSize_t(SR_EventLog* self, const LCHAR* token, size_t value) 420 { 421 SR_EventLogImpl *impl = (SR_EventLogImpl *)self; 422 ESR_ReturnCode rc; 423 LCHAR alpha[MAX_UINT_DIGITS+1]; 424 size_t size = MAX_INT_DIGITS+1; 425 426 if (impl->logLevel == 0) 427 return ESR_SUCCESS; 428 CHK(rc, lultostr(value, alpha, &size, 10)); 429 return self->token(self, token, alpha); 430 CLEANUP: 431 return rc; 432 } 433 434 ESR_ReturnCode SR_EventLog_TokenBool(SR_EventLog* self, const LCHAR* token, ESR_BOOL value) 435 { 436 if (value) 437 return self->token(self, token, L("TRUE")); 438 else 439 return self->token(self, token, L("FALSE")); 440 } 441 442 ESR_ReturnCode SR_EventLog_TokenFloat(SR_EventLog* self, const LCHAR* token, float value) 443 { 444 SR_EventLogImpl *impl = (SR_EventLogImpl *)self; 445 LCHAR alpha[MAX_INT_DIGITS+1]; 446 447 if (impl->logLevel == 0) 448 return ESR_SUCCESS; 449 sprintf(alpha, "%.2f", value); 450 return self->token(self, token, alpha); 451 } 452 453 ESR_ReturnCode logIt(SR_EventLogImpl *impl, LCHAR* evtt, LCHAR* log_record, size_t* writtenSize) 454 { 455 struct tm *ct, ct_r; 456 LCHAR header[128], header2[64]; 457 PTimeStamp timestamp; 458 const size_t sizeof_LCHAR = sizeof(LCHAR); 459 const LCHAR* bar = "|"; 460 const LCHAR* nl = "\n"; 461 size_t i, len; 462 const LCHAR* toWrite[5]; 463 464 toWrite[0] = header; 465 toWrite[1] = bar; 466 toWrite[2] = evtt; 467 toWrite[3] = log_record; 468 toWrite[4] = nl; 469 470 ct = &ct_r; 471 memset(ct, 0, sizeof(struct tm)); 472 473 switch (impl->logFile_state) 474 { 475 case FILE_OK: 476 case SPACE_SETTING: 477 PTimeStampSet(×tamp); 478 ct = localtime_r(×tamp.secs, &ct_r); 479 480 sprintf(header, "TIME=%04d%02d%02d%02d%02d%02d%03d", 481 ct->tm_year + 1900, ct->tm_mon + 1, ct->tm_mday, ct->tm_hour, 482 ct->tm_min, ct->tm_sec, timestamp.msecs); 483 quote_delimiter(header, 128); 484 485 sprintf(header2, "CHAN=%s", L("0")); /* default is channel 0 in ESR */ 486 quote_delimiter(header2, 128); 487 488 LSTRCAT(header, bar); 489 LSTRCAT(header, header2); 490 491 /* write the header,bar,evtt, and record */ 492 for (*writtenSize = 0, i = 0; i < 5; i++) 493 { 494 len = LSTRLEN(toWrite[i]); 495 if (pfwrite(toWrite[i], sizeof_LCHAR, len, impl->logFile)) 496 *writtenSize += len; 497 } 498 499 if (*writtenSize <= 0) 500 { 501 PLogError(L("Could not write to log file; logging halted")); 502 impl->logFile_state = FILE_ERROR; 503 break; 504 } 505 else 506 { 507 pfflush(impl->logFile); 508 } 509 510 break; 511 512 /* If couldn't open file or error previously, just return */ 513 case UNINITIALIZED: 514 case NO_FILE: 515 case FILE_ERROR: 516 case SEEK_ERROR: 517 default: 518 return ESR_INVALID_STATE; 519 520 } 521 522 return ESR_SUCCESS; 523 } 524 525 526 ESR_ReturnCode SR_EventLog_Event(SR_EventLog* self, const LCHAR* event) 527 { 528 SR_EventLogImpl *impl = (SR_EventLogImpl *)self; 529 ESR_ReturnCode rc; 530 long userTime, kernelTime; 531 long cpuTime; 532 LCHAR buf[P_PATH_MAX]; /* allow space for EVNT=<blah> */ 533 size_t writtenSize; 534 535 if (impl == NULL || event == NULL) 536 return ESR_INVALID_ARGUMENT; 537 if (impl->logLevel == 0) 538 return ESR_SUCCESS; 539 540 /* event cannot contain '=' */ 541 if (LSTRCHR(event, L('=')) != NULL) 542 { 543 PLogError(L("SLEE: SR_EventLog_Event: warning: " 544 "SR_EventLog_Event failed. Event '%s' contains illegal '=' " 545 "character\n"), event); 546 return ESR_INVALID_ARGUMENT; 547 } 548 549 CHKLOG(rc, PGetCPUTimes(&userTime, &kernelTime)); 550 551 if (!LSTRCMP(event, "SWIrcst")) 552 { 553 impl->serviceStartUserCPU = userTime; 554 impl->serviceStartKernelCPU = kernelTime; 555 } 556 557 LSTRCPY(buf, event); 558 if (quote_delimiter(buf, LSTRLEN(buf) + 1) != 0) 559 { 560 PLogError(L("ESR_BUFFER_OVERFLOW: '%s' exceeds 8 characters when '|' characters are quoted"), buf); 561 return ESR_BUFFER_OVERFLOW; 562 } 563 564 /* if this event is an end-of-recognition event then check to see if we 565 want to capture this waveform. */ 566 567 if (!LSTRCMP(event, "SWIrcnd")) 568 { 569 /* what to do ??? ALTsleeLogCheckWaveCapture(data); */ 570 } 571 572 cpuTime = userTime - impl->serviceStartUserCPU; 573 SR_EventLogTokenInt(self, L("UCPU"), cpuTime); 574 cpuTime = kernelTime - impl->serviceStartKernelCPU; 575 SR_EventLogTokenInt(self, L("SCPU"), cpuTime); 576 577 578 sprintf(buf, "EVNT=%s", event); 579 /* This call will set writtenSize to be some value >= 0 */ 580 logIt(impl, buf, impl->tokenBuf, &writtenSize); 581 impl->tokenBuf[0] = 0; 582 583 return ESR_SUCCESS; 584 CLEANUP: 585 return rc; 586 } 587 588 ESR_ReturnCode writeRiffHeader(SR_EventLog* self) 589 { 590 SR_EventLogImpl *impl = (SR_EventLogImpl *)self; 591 unsigned int total_buflen; 592 int num_samples; 593 unsigned int bytes_sec; 594 595 RiffHeaderStruct header; 596 597 num_samples = impl->waveform_num_bytes / impl->waveform_bytes_per_sample; 598 599 strncpy(header.riffString, "RIFF", 4); 600 strncpy(header.waveString, "WAVE", 4); 601 strncpy(header.fmtString, "fmt ", 4); 602 strncpy(header.dataString, "data", 4); 603 604 total_buflen = sizeof(RiffHeaderStruct) + impl->waveform_num_bytes; 605 bytes_sec = impl->waveform_sample_rate * impl->waveform_bytes_per_sample; 606 607 header.riffChunkLength = total_buflen - sizeof(ChunkInfoStruct); 608 header.fmtChunkLength = sizeof(WaveFormat); 609 header.waveinfo.nFormatTag = WAVEFORMAT_PCM; /* codec */ 610 header.waveinfo.nChannels = 1; 611 header.waveinfo.nSamplesPerSec = impl->waveform_sample_rate; 612 header.waveinfo.nAvgBytesPerSec = bytes_sec; 613 header.waveinfo.nBlockAlign = (unsigned short) impl->waveform_bytes_per_sample; 614 header.waveinfo.wBitsPerSample = (unsigned short)((bytes_sec * 8) / impl->waveform_sample_rate); 615 header.dataLength = (unsigned int) impl->waveform_num_bytes; 616 617 pfseek(impl->waveformFile, 0, SEEK_SET); 618 619 /* RiffHeaderStruct */ 620 pfwrite(&header.riffString, 1, sizeof(header.riffString), impl->waveformFile); 621 pfwrite(&header.riffChunkLength, sizeof(header.riffChunkLength), 1, impl->waveformFile); 622 pfwrite(&header.waveString, 1, sizeof(header.waveString), impl->waveformFile); 623 pfwrite(&header.fmtString, 1, sizeof(header.fmtString), impl->waveformFile); 624 pfwrite(&header.fmtChunkLength, sizeof(header.fmtChunkLength), 1, impl->waveformFile); 625 626 /* WaveFormat */ 627 pfwrite(&header.waveinfo.nFormatTag, sizeof(header.waveinfo.nFormatTag), 1, impl->waveformFile); 628 pfwrite(&header.waveinfo.nChannels, sizeof(header.waveinfo.nChannels), 1, impl->waveformFile); 629 pfwrite(&header.waveinfo.nSamplesPerSec, sizeof(header.waveinfo.nSamplesPerSec), 1, impl->waveformFile); 630 pfwrite(&header.waveinfo.nAvgBytesPerSec, sizeof(header.waveinfo.nAvgBytesPerSec), 1, impl->waveformFile); 631 pfwrite(&header.waveinfo.nBlockAlign, sizeof(header.waveinfo.nBlockAlign), 1, impl->waveformFile); 632 pfwrite(&header.waveinfo.wBitsPerSample, sizeof(header.waveinfo.wBitsPerSample), 1, impl->waveformFile); 633 634 /* Continuation of RiffHeaderStruct */ 635 pfwrite(&header.dataString, 1, sizeof(header.dataString), impl->waveformFile); 636 pfwrite(&header.dataLength, sizeof(header.dataLength), 1, impl->waveformFile); 637 638 return ESR_SUCCESS; 639 } 640 641 ESR_ReturnCode SR_EventLog_AudioOpen(SR_EventLog* self, const LCHAR* audio_type, size_t sample_rate, size_t sample_size) 642 { 643 SR_EventLogImpl *impl = (SR_EventLogImpl*) self; 644 LCHAR *p; 645 646 LSTRCPY(impl->waveformFilename, impl->logFilename); 647 p = LSTRSTR(impl->waveformFilename, L(".log")); 648 if (p == NULL) 649 { 650 PLogError(L("ESR_OPEN_ERROR: %s"), impl->waveformFilename); 651 return ESR_OPEN_ERROR; 652 } 653 *p = 0; /* trunc the name */ 654 655 psprintf(impl->waveformFilename, L("%s-%04lu.wav"), impl->waveformFilename, (unsigned long) ++impl->waveformCounter); 656 657 impl->waveformFile = pfopen ( impl->waveformFilename, L("wb+") ); 658 659 if (impl->waveformFile == NULL) 660 { 661 PLogError(L("ESR_OPEN_ERROR: %s"), impl->waveformFilename); 662 return ESR_OPEN_ERROR; 663 } 664 impl->waveform_num_bytes = 0; 665 impl->waveform_bytes_per_sample = sample_size; 666 impl->waveform_sample_rate = sample_rate; 667 return writeRiffHeader(self); 668 } 669 670 ESR_ReturnCode SR_EventLog_AudioClose(SR_EventLog* self) 671 { 672 SR_EventLogImpl *impl = (SR_EventLogImpl*) self; 673 ESR_ReturnCode rc; 674 675 /* impl->waveform_num_bytes has likely grown so we need to update the header before closing the file */ 676 CHKLOG(rc, writeRiffHeader(self)); 677 if (pfclose(impl->waveformFile)) 678 { 679 rc = ESR_CLOSE_ERROR; 680 PLogError(ESR_rc2str(rc)); 681 goto CLEANUP; 682 } 683 impl->waveformFile = NULL; 684 return ESR_SUCCESS; 685 CLEANUP: 686 return rc; 687 } 688 689 ESR_ReturnCode SR_EventLog_AudioWrite(SR_EventLog* self, void* buffer, size_t num_bytes) 690 { 691 SR_EventLogImpl *impl = (SR_EventLogImpl*) self; 692 ESR_ReturnCode rc; 693 size_t size = num_bytes / impl->waveform_bytes_per_sample; 694 695 if (num_bytes > 0 && pfwrite(buffer, impl->waveform_bytes_per_sample, size, impl->waveformFile) != size) 696 { 697 LCHAR cwd[P_PATH_MAX]; 698 size_t len; 699 700 len = P_PATH_MAX; 701 CHKLOG(rc, pf_get_cwd (cwd, &len)); 702 PLogError(L("ESR_WRITE_ERROR: %s, cwd=%s"), impl->waveformFilename, cwd); 703 return ESR_WRITE_ERROR; 704 } 705 706 impl->waveform_num_bytes += num_bytes; 707 return ESR_SUCCESS; 708 CLEANUP: 709 return rc; 710 } 711 712 ESR_ReturnCode SR_EventLog_AudioGetFilename(SR_EventLog* self, LCHAR* waveformFilename, size_t* len) 713 { 714 SR_EventLogImpl *impl = (SR_EventLogImpl*) self; 715 716 if (waveformFilename == NULL) 717 { 718 PLogError(L("ESR_INVALID_ARGUMENT")); 719 return ESR_INVALID_ARGUMENT; 720 } 721 722 if (*len < LSTRLEN(impl->waveformFilename)) 723 { 724 PLogError(L("ESR_BUFFER_OVERFLOW")); 725 return ESR_BUFFER_OVERFLOW; 726 } 727 728 LSTRCPY(waveformFilename, impl->waveformFilename); 729 *len = LSTRLEN(waveformFilename); 730 return ESR_SUCCESS; 731 } 732 733 ESR_ReturnCode SR_EventLogEventSessionImpl(SR_EventLog* self) 734 { 735 size_t size, i, j; 736 ESR_ReturnCode rc; 737 LCHAR* key; 738 LCHAR lValue[256]; 739 int iValue; 740 float fValue; 741 size_t size_tValue; 742 asr_uint16_t asr_uint16_tValue; 743 ESR_BOOL bValue; 744 IntArrayList* list; 745 VariableTypes type; 746 size_t lValueSize = 256; 747 size_t len; 748 749 CHKLOG(rc, ESR_SessionGetSize(&size)); 750 for (i = 0; i < size; ++i) 751 { 752 CHKLOG(rc, ESR_SessionGetKeyAtIndex(i, &key)); 753 CHKLOG(rc, ESR_SessionGetPropertyType(key, &type)); 754 755 switch (type) 756 { 757 case TYPES_INT: 758 CHKLOG(rc, ESR_SessionGetInt(key, &iValue)); 759 CHKLOG(rc, SR_EventLogTokenInt(self, key, iValue)); 760 break; 761 case TYPES_UINT16_T: 762 CHKLOG(rc, ESR_SessionGetUint16_t(key, &asr_uint16_tValue)); 763 CHKLOG(rc, SR_EventLogTokenUint16_t(self, key, asr_uint16_tValue)); 764 break; 765 case TYPES_SIZE_T: 766 CHKLOG(rc, ESR_SessionGetSize_t(key, &size_tValue)); 767 CHKLOG(rc, SR_EventLogTokenSize_t(self, key, size_tValue)); 768 break; 769 case TYPES_BOOL: 770 CHKLOG(rc, ESR_SessionGetBool(key, &bValue)); 771 CHKLOG(rc, SR_EventLogTokenBool(self, key, bValue)); 772 break; 773 case TYPES_FLOAT: 774 CHKLOG(rc, ESR_SessionGetFloat(key, &fValue)); 775 CHKLOG(rc, SR_EventLogTokenFloat(self, key, fValue)); 776 break; 777 case TYPES_PLCHAR: 778 len = 256; 779 CHKLOG(rc, ESR_SessionGetLCHAR(key, (LCHAR*) &lValue, &len)); 780 CHKLOG(rc, SR_EventLogToken(self, key, (LCHAR*) lValue)); 781 break; 782 783 case TYPES_INTARRAYLIST: 784 CHKLOG(rc, ESR_SessionGetProperty(key, (void **)&list, TYPES_INTARRAYLIST)); 785 CHKLOG(rc, list->getSize(list, &len)); 786 CHKLOG(rc, SR_EventLogTokenInt(self, key, len)); 787 for (j = 0; j < len; ++j) 788 { 789 CHKLOG(rc, list->get(list, j, &iValue)); 790 lValueSize = sizeof(lValue); 791 CHKLOG(rc, litostr(j, lValue, &lValueSize, 10)); 792 CHKLOG(rc, SR_EventLogTokenInt(self, lValue, iValue)); 793 } 794 break; 795 796 default: 797 /* do nothing */ 798 ; 799 } 800 } 801 CHKLOG(rc, SR_EventLogEvent(self, L("ESRsession"))); 802 return ESR_SUCCESS; 803 CLEANUP: 804 return rc; 805 } 806