• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&timestamp);
239     ct = localtime_r(&timestamp.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(&timestamp);
478       ct = localtime_r(&timestamp.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