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 */
PGetCPUTimes(long * userTime,long * kernelTime)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
propertyChanged(ESR_SessionTypeListener * self,const LCHAR * name,const void * oldValue,const void * newValue,VariableTypes variableType,void * data)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
SR_EventLogCreate(SR_EventLog ** self)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
SR_EventLog_Destroy(SR_EventLog * self)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
quote_delimiter(LCHAR * record,size_t len)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
SR_EventLog_Token(SR_EventLog * self,const LCHAR * token,const LCHAR * value)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
SR_EventLog_TokenInt(SR_EventLog * self,const LCHAR * token,int value)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
SR_EventLog_TokenUint16_t(SR_EventLog * self,const LCHAR * token,asr_uint16_t value)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
SR_EventLog_TokenSize_t(SR_EventLog * self,const LCHAR * token,size_t value)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
SR_EventLog_TokenBool(SR_EventLog * self,const LCHAR * token,ESR_BOOL value)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
SR_EventLog_TokenFloat(SR_EventLog * self,const LCHAR * token,float value)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
logIt(SR_EventLogImpl * impl,LCHAR * evtt,LCHAR * log_record,size_t * writtenSize)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
SR_EventLog_Event(SR_EventLog * self,const LCHAR * event)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
writeRiffHeader(SR_EventLog * self)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
SR_EventLog_AudioOpen(SR_EventLog * self,const LCHAR * audio_type,size_t sample_rate,size_t sample_size)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
SR_EventLog_AudioClose(SR_EventLog * self)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
SR_EventLog_AudioWrite(SR_EventLog * self,void * buffer,size_t num_bytes)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
SR_EventLog_AudioGetFilename(SR_EventLog * self,LCHAR * waveformFilename,size_t * len)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
SR_EventLogEventSessionImpl(SR_EventLog * self)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