Home | History | Annotate | Download | only in src
      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(&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 
    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(&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 
    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