/*
 * DSP_WAFI2_WIN32: DSP Blockset S-function implementing
 *                  wave audio file input
 *
 * Original implementation by:
 *      Steve Mitchell
 *      Department of Electrical Engineering
 *      Cornell University
 *      Ithaca, NY  14853
 *
 * Code adapted and used by permission from the
 * Cornell Laboratory of Ornithology at Cornell University.
 *
 * Initial DSP Blockset version: D. Orofino
 * Copyright 1995-2000 The MathWorks, Inc.
 * $Revision: 1.6 $ $Date: 2000/07/11 19:29:10 $
 */

#include <windows.h>
#include <mmsystem.h>
#include <stdio.h>

#include "dsp_sim.h"

enum {
    FILENAME_ARGC,	    /* ".wav" will be added		     */
    SAMPLE_RATE_ARGC,	    /* in Hertz			             */
    NUM_CHANNELS_ARGC,	    /* 1 = mono, 2 = stereo		     */
    SAMPLES_PER_FRAME_ARGC, /* Samples per frame		     */
    MIN_BUFFER_SIZE_ARGC,   /* Minimum buffer size to read from file */
    NUM_ARGS
};

#define FILENAME_ARG(S)          ssGetSFcnParam(S, FILENAME_ARGC)
#define SAMPLE_RATE_ARG(S)       ssGetSFcnParam(S, SAMPLE_RATE_ARGC)
#define NUM_CHANNELS_ARG(S)      ssGetSFcnParam(S, NUM_CHANNELS_ARGC)
#define SAMPLES_PER_FRAME_ARG(S) ssGetSFcnParam(S, SAMPLES_PER_FRAME_ARGC)
#define MIN_BUFFER_SIZE_ARG(S)      ssGetSFcnParam(S, MIN_BUFFER_SIZE_ARGC)

#define SAMPLE_RATE_ARGV	(*mxGetPr(SAMPLE_RATE_ARG(S)))
#define NUM_CHANNELS_ARGV	(int_T)(*mxGetPr(NUM_CHANNELS_ARG(S)))
#define SAMPLES_PER_FRAME_ARGV	(int_T)(*mxGetPr(SAMPLES_PER_FRAME_ARG(S)))
#define MIN_BUFFER_SIZE_ARGV 	(int_T)(*mxGetPr(MIN_BUFFER_SIZE_ARG(S)))

#define OUTPORT 0

/* Structure for local block-instance resource-handling */
typedef struct
{
    boolean_T buffersAllocated;
    boolean_T wavFileOpen;
    boolean_T dataCacheConstructed;
} AllocatedResources;

typedef struct {
    AllocatedResources res;
} SFcnDWorkCache;

#define NUM_WAFI2_RESOURCES 3

enum {
    ALLOCATED_RESOURCE_DWORK_CACHE,
    NUM_DWORK
};

/* Integer work vector items */
enum {
	kBYTES_IN_FILE,	      	/* Number of samples in file			*/
	kBITS_PER_SAMPLE,	/* Number of bits per sample in file		*/
	kSAMPLE_RATE,		/* Samples per second				*/
	kNUM_CHANNELS,		/* Number of channels (1=mono, 2=stereo)	*/
	kBLOCK_ALIGN,		/* Bytes per sample * num channels		*/
	kIN_WAVE_CHUNK,		/* Boolean -- in wave chunk			*/
	kIN_DATA_CHUNK,		/* Boolean -- in data chunk			*/
	kNUMIWORKITEMS
};

#define BYTES_IN_FILE   (ssGetIWorkValue(S, kBYTES_IN_FILE))
#define BITS_PER_SAMPLE (ssGetIWorkValue(S, kBITS_PER_SAMPLE))
#define SAMPLE_RATE     (ssGetIWorkValue(S, kSAMPLE_RATE))
#define NUM_CHANNELS    (ssGetIWorkValue(S, kNUM_CHANNELS))
#define BLOCK_ALIGN     (ssGetIWorkValue(S, kBLOCK_ALIGN))
#define IN_WAVE_CHUNK   (ssGetIWorkValue(S, kIN_WAVE_CHUNK))
#define IN_DATA_CHUNK   (ssGetIWorkValue(S, kIN_DATA_CHUNK))

/* Pointer work vector items */
enum {
	kDATA_CACHE,	/* Data cache structure   */
	kFILE_DESC,	/* File descriptor	  */
	kFILENAME,	/* C-string file name	  */
	kDATA_CK_INFO,	/* Data chunk info record */
	kWAVE_CK_INFO,	/* Wave chunk info record */
	kNUMPWORKITEMS
};

#define FILE_DESC	((HMMIO)       (ssGetPWorkValue(S, kFILE_DESC)))
#define FILENAME	((char*)       (ssGetPWorkValue(S, kFILENAME)))
#define DATA_CK_INFO	((MMCKINFO*)   (ssGetPWorkValue(S, kDATA_CK_INFO)))
#define WAVE_CK_INFO	((MMCKINFO*)   (ssGetPWorkValue(S, kWAVE_CK_INFO)))

#define BYTES_PER_SAMPLE	   (uint_T)((BITS_PER_SAMPLE + 7) / 8)
#define SAMPLES_PER_CHAN_IN_CACHE  (uint_T)(MAX(SAMPLES_PER_FRAME_ARGV, MIN_BUFFER_SIZE_ARGV))
#define TOTAL_BYTES_IN_CACHE       (uint_T)(SAMPLES_PER_CHAN_IN_CACHE * BLOCK_ALIGN)

#define MMERROR	(mmResult != MMSYSERR_NOERROR)

static void FreeBuffer(SimStruct *S)
{
    {
        char *filename = FILENAME;
        if (filename != NULL) free(filename);
        ssSetPWorkValue(S, kFILENAME, NULL);
    }
    {
	MMCKINFO *dataCkInfo = DATA_CK_INFO;
        if (dataCkInfo != NULL) {
            free (dataCkInfo);
        }
	ssSetPWorkValue(S, kDATA_CK_INFO, NULL);
    }
    {
	MMCKINFO *waveCkInfo = WAVE_CK_INFO;
	if (waveCkInfo != NULL) free(waveCkInfo);
	ssSetPWorkValue(S, kWAVE_CK_INFO, NULL);
    }
}


static void CloseFile(SimStruct *S)
{
    if (FILE_DESC != NULL) {
        mmioClose(FILE_DESC, 0);
    }
    ssSetPWorkValue(S, kFILE_DESC, NULL);
}


/*
 * ===================================================
 * BEGIN - Data Cache functions
 * ===================================================
 */

typedef struct {

    byte_T *data;           /* Pointer to allocated memory       */
    int_T   SizeInSamples;  /* Total number of samples in cache  */
    int_T   SampleCount;    /* Bytes per sample                  */
    int_T   BytesPerSample; /* Total number of bytes in cache    */

} DataCache;


/* Function: FreeDataCache ====================================================
 * Abstract:
 *		Free data buffer and then free data cache structure.
 */
static void FreeDataCache(SimStruct *S)
{
    DataCache *data_cache = (DataCache*)(ssGetPWorkValue (S, kDATA_CACHE));
    if (data_cache != NULL) {
        if (data_cache->data != NULL) free(data_cache->data);
        free(data_cache);
        ssSetPWorkValue(S, kDATA_CACHE, NULL);
    }
}


/* Function: CleanUp ===============================================
 * Abstract:
 *		Free memory and close files
 */
static void CleanUp(SimStruct *S)
{
    CloseFile(S);
    FreeBuffer(S);
    FreeDataCache(S);
}


/* Function: ConstructDataCache ================================================
 * Abstract:
 *		Construct Data Cache
 */
static void ConstructDataCache(SimStruct *S)
{
    /* Allocate data cache and sample buffer */
    DataCache *data_cache = (DataCache *)calloc(1, sizeof(DataCache));
    
    /* We must set the PWork before CleanUp can be called. */
    ssSetPWorkValue(S, kDATA_CACHE, data_cache);  
    
    if (data_cache == NULL) {
        CleanUp(S);
        THROW_ERROR(S, "Failed to allocate data cache.")
    }
    
    data_cache->data = (byte_T *)calloc(TOTAL_BYTES_IN_CACHE, sizeof(byte_T));
    
    if (data_cache->data == NULL) {
        CleanUp(S);
        THROW_ERROR(S, "Failed to allocate data cache.")
    }

    data_cache->SizeInSamples  = SAMPLES_PER_CHAN_IN_CACHE * NUM_CHANNELS;
    data_cache->SampleCount    = 0;
    data_cache->BytesPerSample = BYTES_PER_SAMPLE;
}


/* Function: FillDataCacheFromFile ====================================================
 * Abstract:
 *		Fill the portion of the cache that does not have available samples
 *              Preserve any "available" samples in the cache.
 */
static void FillDataCacheFromFile(SimStruct *S, DataCache *cache)
{    
    /* Read next buffer of samples from file: */
    const int_T   emptySamples       = cache->SizeInSamples - cache->SampleCount;
    const int_T   nBytesToRead       = emptySamples * cache->BytesPerSample;
    const int_T   nDataBytesInCache  = cache->SampleCount * cache->BytesPerSample;

    int_T         nBytesInFile       = BYTES_IN_FILE;
    int_T         nBytesReadFromFile = 0;
    byte_T       *sampleBuf          = cache->data;

    /* Push unread samples to beginning of buffer.
     * Then fill the rest of the buffer from the file.
     */
    if(nDataBytesInCache > 0) {
        memcpy(sampleBuf, sampleBuf+nBytesToRead, nDataBytesInCache);
        sampleBuf += nDataBytesInCache;
    }

    if (nBytesInFile > 0) {
        /* Read integer samples from file */

        nBytesReadFromFile = mmioRead(FILE_DESC, sampleBuf, MIN(nBytesInFile, nBytesToRead));
        if (nBytesReadFromFile == -1) {
	    CleanUp(S);
	    THROW_ERROR(S, "Error reading file");

        }
        /* Decrement file sample count and save it in an IWork */
        nBytesInFile -= nBytesReadFromFile;
        ssSetIWorkValue(S, kBYTES_IN_FILE, nBytesInFile);
    }

    if (nBytesReadFromFile < nBytesToRead) {
        /*
         * If file is exhausted, append zeros, and record last value
        * (used to prevent glitching)
        */
        unsigned char zero = (BITS_PER_SAMPLE == 8) ? 128 : 0;
        memset(sampleBuf + nBytesReadFromFile, zero, nBytesToRead - nBytesReadFromFile);
    }
}


/* Function: GetNSamplesFromCache ====================================================
 * Abstract:
 *		Return pointer to buffer of Samples requested.
 *              Fill buffer first if necessary.
 */
static byte_T * GetNSamplesFromCache(SimStruct *S, int_T samplesRequested)
{
    DataCache *cache     = (DataCache*)(ssGetPWorkValue(S, kDATA_CACHE));
    byte_T    *sampleBuf = cache->data;

    if(cache->SampleCount < samplesRequested) {

        FillDataCacheFromFile(S, cache);

        cache->SampleCount = cache->SizeInSamples - samplesRequested;

    } else {

         /* Set pointer to beginning of Unread bytes */
         const int_T  EmptySamplesInCache = cache->SizeInSamples - cache->SampleCount;
         sampleBuf = cache->data + EmptySamplesInCache * cache->BytesPerSample;

         /* Decrement count of available samples */
         cache->SampleCount -= samplesRequested;
    }

    return sampleBuf;
}


static void ReadWavHeader(SimStruct *S)
{
    MMRESULT		mmResult;
    MMCKINFO		mmFMTCkInfo;
    PCMWAVEFORMAT	pcmWaveFormat;
    long		m;
    static char		msg[256];

    /* Find 'WAVE' chunk */
    WAVE_CK_INFO->fccType = mmioFOURCC('W', 'A', 'V', 'E'); 
    mmResult = mmioDescend(FILE_DESC, WAVE_CK_INFO, NULL, MMIO_FINDRIFF);
    if (MMERROR) {
        CleanUp(S);
        THROW_ERROR(S,"Error reading file header")
    }

    ssSetIWorkValue(S, kIN_WAVE_CHUNK, 1);

    /* Find 'fmt ' chunk */
    mmFMTCkInfo.ckid = mmioFOURCC('f', 'm', 't', ' '); 
    mmResult = mmioDescend(FILE_DESC, &mmFMTCkInfo, WAVE_CK_INFO, MMIO_FINDCHUNK);
    if (MMERROR) {
        CleanUp(S);
        THROW_ERROR(S,"Error reading file header")
    }
    
    /* Read 'fmt ' chunk */
    m = mmioRead(FILE_DESC, (char *) &pcmWaveFormat, sizeof(PCMWAVEFORMAT));
    if (m != sizeof(PCMWAVEFORMAT)) {
        CleanUp(S);
	THROW_ERROR(S,"Error reading file header")
    }

    /* Ascend from format chunk */
    mmResult = mmioAscend(FILE_DESC, &mmFMTCkInfo, 0); 
    if (MMERROR) {
        CleanUp(S);
	THROW_ERROR(S, "Error reading file header")
    }

    /* Check for PCM format */
    if (pcmWaveFormat.wf.wFormatTag != WAVE_FORMAT_PCM) {
        CleanUp(S);
	THROW_ERROR(S, "Wave file is not in PCM format")
    }

    /* Read PCM format parameters */
    ssSetIWorkValue(S, kBITS_PER_SAMPLE, pcmWaveFormat.wBitsPerSample);
    ssSetIWorkValue(S, kSAMPLE_RATE,     pcmWaveFormat.wf.nSamplesPerSec);
    ssSetIWorkValue(S, kNUM_CHANNELS,    pcmWaveFormat.wf.nChannels);
    ssSetIWorkValue(S, kBLOCK_ALIGN,     pcmWaveFormat.wf.nBlockAlign);

    /* Verify numChannels and sampleRate arguments */
    if (NUM_CHANNELS_ARGV != NUM_CHANNELS) {
        CleanUp(S);
	sprintf(msg, "File contains wrong number of channels (%ld)", NUM_CHANNELS);
	THROW_ERROR(S, msg)
    }

    if (SAMPLE_RATE_ARGV != SAMPLE_RATE) {
        CleanUp(S);
	sprintf(msg, "File has wrong sample rate (%ld Hz)", SAMPLE_RATE);
	THROW_ERROR(S, msg)
    }

    /* Find 'data' chunk */
    DATA_CK_INFO->ckid = mmioFOURCC('d', 'a', 't', 'a'); 
    mmResult = mmioDescend(FILE_DESC, DATA_CK_INFO, WAVE_CK_INFO, MMIO_FINDCHUNK);
    if (MMERROR) {
        CleanUp(S);
	THROW_ERROR(S, "Error reading file header")
    }

    ssSetIWorkValue(S, kIN_DATA_CHUNK, 1);
    ssSetIWorkValue(S, kBYTES_IN_FILE, DATA_CK_INFO->cksize);
}


static void OpenWavFile(SimStruct *S)
{
    /* Check file name */
    if (strlen (FILENAME) > 127) {
        CleanUp(S);
	THROW_ERROR(S, "File name too long")
    }

    if (strchr (FILENAME, '+') != NULL) {
        CleanUp(S);
	THROW_ERROR(S, "File name must not contain a \"+\" character")
    }

    /* Open file */
    {
	HMMIO fp = mmioOpen(FILENAME, NULL, MMIO_READ | MMIO_ALLOCBUF);
	if (fp == NULL) {
            CleanUp(S);
	    THROW_ERROR(S, "Error opening file");
	}
	ssSetPWorkValue (S, kFILE_DESC, fp);
    }
    
    /* Read header */
    ReadWavHeader(S); RETURN_IF_ERROR(S);

    /* Set internal buffer size 
     *
     * Note: for faster access, supply a custom I/O buffer 
     *       and access with mmioAdvance()
     */
    {
	MMRESULT  mmResult = mmioSetBuffer(FILE_DESC, NULL, TOTAL_BYTES_IN_CACHE, 0);
	if (MMERROR) {
            CleanUp(S);
	    THROW_ERROR(S, "Error opening file")
	}
    }
}


static void InitPointers(SimStruct *S)
{
    ssSetPWorkValue(S, kDATA_CACHE,   NULL);
    ssSetPWorkValue(S, kFILE_DESC,    NULL);
    ssSetPWorkValue(S, kFILENAME,     NULL);
    ssSetPWorkValue(S, kDATA_CK_INFO, NULL);
    ssSetPWorkValue(S, kWAVE_CK_INFO, NULL);

    ssSetIWorkValue(S, kIN_WAVE_CHUNK, 0);
    ssSetIWorkValue(S, kIN_DATA_CHUNK, 0);
}


/* Function: MakeBuffer =======================================================
 * Abstract:
 *		Allocate file name, and FILE
 * Can fail.
 */
static void MakeBuffer(SimStruct *S)
{
    char *filename;
    long  filename_len;
    
    /* Allocate file name */
    filename_len = 1 + mxGetNumberOfElements(FILENAME_ARG(S));
    filename = (char *)calloc(filename_len, sizeof(char));
    if (filename == NULL) {
        CleanUp(S);
	THROW_ERROR(S, "Error allocating memory")
    }
    ssSetPWorkValue (S, kFILENAME, filename);

    /* Initialize filename */
    if (mxGetString(FILENAME_ARG(S), filename, filename_len)) {
        CleanUp(S);
	THROW_ERROR(S, "File name must be a string")
    }
    
    {
	MMCKINFO *dataCkInfo, *waveCkInfo;

	/* Allocate data chunk info */
	dataCkInfo = (MMCKINFO *)calloc(1, sizeof (MMCKINFO));
	if (dataCkInfo == NULL) {
            CleanUp(S);
	    THROW_ERROR(S, "Error allocating memory")
	}
	ssSetPWorkValue (S, kDATA_CK_INFO, dataCkInfo);

	/* Allocate wave chunk info */
	waveCkInfo = (MMCKINFO *)calloc(1, sizeof (MMCKINFO));
	if (waveCkInfo == NULL) {
            CleanUp(S);
	    THROW_ERROR(S, "Error allocating memory")
	}
	ssSetPWorkValue (S, kWAVE_CK_INFO, waveCkInfo);
    }
}


# if defined(MATLAB_MEX_FILE)
# define MDL_CHECK_PARAMETERS
static void mdlCheckParameters(SimStruct *S)
{
    if(!IS_SCALAR(SAMPLE_RATE_ARG(S))) 
        THROW_ERROR(S, "Sample Frequency must be a positive scalar");

    if(OK_TO_CHECK_VAR(S, SAMPLES_PER_FRAME_ARG(S))) {
        if(!IS_SCALAR(SAMPLES_PER_FRAME_ARG(S)))
            THROW_ERROR(S, "Samples per frame must be a positive scalar");
    }

    if(OK_TO_CHECK_VAR(S, MIN_BUFFER_SIZE_ARG(S))) {
        if(!IS_SCALAR(MIN_BUFFER_SIZE_ARG(S)))
            THROW_ERROR(S, "Minimum number of samples for each read from file must be a positive integer");
    }

    if(!IS_FLINT_IN_RANGE(NUM_CHANNELS_ARG(S),1,2))
        THROW_ERROR(S, "Number of channels must be 1 or 2");

    if (!mxIsChar(FILENAME_ARG(S)))
        THROW_ERROR(S, "File Name must be a string");
}
# endif


static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, NUM_ARGS);

#if defined(MATLAB_MEX_FILE)
    if(ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) return;
    mdlCheckParameters(S);
    RETURN_IF_ERROR(S);
#endif

    ssSetSFcnParamNotTunable(S, FILENAME_ARGC);
    ssSetSFcnParamNotTunable(S, SAMPLE_RATE_ARGC);
    ssSetSFcnParamNotTunable(S, NUM_CHANNELS_ARGC);
    ssSetSFcnParamNotTunable(S, SAMPLES_PER_FRAME_ARGC);
    ssSetSFcnParamNotTunable(S, MIN_BUFFER_SIZE_ARGC);

    if (!ssSetNumInputPorts( S, 0)) return;

    if (!ssSetNumOutputPorts(S, 1)) return;
    
    {
        int_T spf = 1;  /* Default samples per frame */
        if(OK_TO_CHECK_VAR(S, SAMPLES_PER_FRAME_ARG(S))) {
            spf = SAMPLES_PER_FRAME_ARGV;
        }
        ssSetOutputPortMatrixDimensions(S,OUTPORT, spf, NUM_CHANNELS_ARGV);
        ssSetOutputPortFrameData(S,OUTPORT, (spf==1) ? FRAME_NO : FRAME_YES);
    }

    ssSetNumSampleTimes(S, 1);
    ssSetNumIWork(      S, kNUMIWORKITEMS);
    ssSetNumPWork(      S, kNUMPWORKITEMS);

    if(!ssSetNumDWork(      S, NUM_DWORK)) return;
    ssSetDWorkWidth(        S, ALLOCATED_RESOURCE_DWORK_CACHE, NUM_WAFI2_RESOURCES);
    ssSetDWorkName(         S, ALLOCATED_RESOURCE_DWORK_CACHE, "allocRsrcsCache");
    ssSetDWorkDataType(     S, ALLOCATED_RESOURCE_DWORK_CACHE, SS_BOOLEAN);
    ssSetDWorkComplexSignal(S, ALLOCATED_RESOURCE_DWORK_CACHE, COMPLEX_NO);

    ssSetOptions(S, SS_OPTION_RUNTIME_EXCEPTION_FREE_CODE |
                    SS_OPTION_CALL_TERMINATE_ON_EXIT);

    /* IMPORTANT NOTE! mdlTerminate can be called at any point now... */
}


static void mdlInitializeSampleTimes (SimStruct *S)
{
    real_T ts = 1;  /* Default sample rate       */
    int_T spf = 1;  /* Default samples per frame */

    if(OK_TO_CHECK_VAR(S, SAMPLES_PER_FRAME_ARG(S))) {
        spf = SAMPLES_PER_FRAME_ARGV;
    }
    
    if(OK_TO_CHECK_VAR(S, SAMPLE_RATE_ARG(S))) {
        ts = SAMPLE_RATE_ARGV;
    }

    ssSetSampleTime (S, 0, (double) spf / ts);
    ssSetOffsetTime (S, 0, 0.0);
}


#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S)
{

}


#define MDL_START
static void mdlStart (SimStruct *S)
{
    SFcnDWorkCache *cache = (SFcnDWorkCache *)ssGetDWork(S, ALLOCATED_RESOURCE_DWORK_CACHE);

    /* Initialize the resource cache for case of an early mdlTerminate call */
    cache->res.buffersAllocated     = false;
    cache->res.wavFileOpen          = false;
    cache->res.dataCacheConstructed = false;

    /* Instance-specific work-vector pointers */
    InitPointers(S);

    /*
    ** Allocate resources necessary for using this S-function,
    ** and set appropriate flags associated with each resource.
    */

    /* Initialize filename & waveheader */
    MakeBuffer(S);
    RETURN_IF_ERROR(S);
    cache->res.buffersAllocated = true;
    
    /* Open file  & read header */
    OpenWavFile(S);
    RETURN_IF_ERROR(S);
    cache->res.wavFileOpen = true;
    
    ConstructDataCache(S); RETURN_IF_ERROR(S);
    cache->res.dataCacheConstructed = true;
}


static void mdlOutputs (SimStruct *S, int_T tid)
{
    /* Convert input to integer samples */
    const int_T  numChannels = NUM_CHANNELS;
    const int_T  numSamples  = SAMPLES_PER_FRAME_ARGV;
    real_T      *y           = ssGetOutputPortSignal (S, OUTPORT);
    
    /*
     * Output samples from buffer:
     */
    switch (BITS_PER_SAMPLE) {
    case 8:
        {
            unsigned char *buf = (unsigned char *)GetNSamplesFromCache(S, numSamples * numChannels);
            int_T i;
            for (i=0; i < numSamples; i++) {
                int_T channel;
                for (channel=0; channel < numChannels; channel++) {
                    y[numSamples * channel + i] = (*buf++ - 128.0) / 128.0;
                }
            }
        }
        break;
        
    case 16:
        {
            short *buf = (short *)GetNSamplesFromCache(S, numSamples * numChannels);
            int_T i;
            for (i=0; i < numSamples; i++) {
                int_T channel;
                for (channel=0; channel < numChannels; channel++) {
                    y[numSamples * channel + i] = *buf++ / 32768.0;
                }
            }
        }
        break;
    }
}


static void mdlTerminate (SimStruct *S)
{
    SFcnDWorkCache *cache =
        (SFcnDWorkCache *)ssGetDWork(S, ALLOCATED_RESOURCE_DWORK_CACHE);

    if (cache != NULL) {
        /* Note - need to tear each of these down in the order in which they were */
        /* initially allocated, since there may be a premature terminate due to   */
        /* an error out of our control (i.e. initiated by Simulink from another   */
        /* part of a model during init/compile time).  Need to handle all cases!  */
        if (cache->res.dataCacheConstructed) {
            MMRESULT mmResult;
            
            /* Exit data chunk */
            if (IN_DATA_CHUNK && (DATA_CK_INFO != NULL) && (FILE_DESC != NULL)) {
                mmResult = mmioAscend(FILE_DESC, DATA_CK_INFO, 0);
                if (MMERROR) {
                    CleanUp(S);
                    THROW_ERROR(S, "Error exiting data chunk")
                }
                ssSetIWorkValue(S, kIN_DATA_CHUNK, 0);
            }
            
            /* Exit WAVE chunk */
            if (IN_WAVE_CHUNK && (WAVE_CK_INFO != NULL) && (FILE_DESC != NULL)) {
                mmResult = mmioAscend(FILE_DESC, WAVE_CK_INFO, 0);
                if (MMERROR) {
                    CleanUp(S);
                    THROW_ERROR(S, "Error exiting wave chunk")
                }
                ssSetIWorkValue(S, kIN_WAVE_CHUNK, 0);
            }

            CleanUp(S);
        }
        else if (cache->res.wavFileOpen) {
            /* Buffer allocated and WAV file open, */
            /* but data cache NOT allocated...     */
            CloseFile(S);
            FreeBuffer(S);
        }
        else if (cache->res.buffersAllocated) {
            /* Only buffer allocated (WAV file NOT open */
            /* and data cache NOT allocated).           */
            FreeBuffer(S);
        }
    }
}

#include "dsp_trailer.c"

/* [EOF] dsp_wafi2_win32.c */
