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 */
62ESR_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
98ESR_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;
146CLEANUP:
147  return rc;
148}
149
150/**
151 * Implementation copied from original Ian Fox implementation of ALTsleeLog
152 */
153
154ESR_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;
284CLEANUP:
285  if (impl->logFile)
286    pfclose (impl->logFile);
287  return rc;
288}
289
290ESR_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;
306CLEANUP:
307  return rc;
308}
309
310
311static 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
335ESR_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
389ESR_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);
400CLEANUP:
401  return rc;
402}
403
404ESR_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);
415CLEANUP:
416  return rc;
417}
418
419ESR_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);
430CLEANUP:
431  return rc;
432}
433
434ESR_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
442ESR_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
453ESR_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
526ESR_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;
584CLEANUP:
585  return rc;
586}
587
588ESR_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
641ESR_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
670ESR_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;
685CLEANUP:
686  return rc;
687}
688
689ESR_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;
708CLEANUP:
709  return rc;
710}
711
712ESR_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
733ESR_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;
803CLEANUP:
804  return rc;
805}
806