/***************************************************************************
    Windows CE Sound Routine
 ***************************************************************************/
#include "stdafx.h"
#include "mmSystem.h"
#include "CE.h"
#include "CEsound.h"

#include "ppp_stdio.h"

/***************************************************************************
    Internal structures
 ***************************************************************************/
struct tSound_private {
    WAVEOUTCAPS     m_Caps;
    HWAVEOUT        m_hWaveOut;
    WAVEHDR       * m_WaveHdrs;
  
    int             m_nSampleRate;
    int             m_nSampleBits;
    int             m_nChannels;
    int             m_nBufSize;
    int             m_nBufCount;
    int             m_nCurrent;
    int             m_nWrote;

    int             m_nStatus;
};

/***************************************************************************
    Internal variables
 ***************************************************************************/
static struct tSound_private This;


/***************************************************************************
    External variables
 ***************************************************************************/
extern bool systemSoundOn;

/***************************************************************************
    Functions
 ***************************************************************************/
int    CESound_Init(int SRate, int SBit, int Channels, int BufSize, int BufCount, void * threadfunc );
void   CESound_Trash(void);
void   CESound_Write(void);
void   CESound_Thread(void);
int    CESound_isBufferFull(void);
char * CESound_BufferPtr(void);

/*************************************************************/
/* waveOutOpen callback function
/*************************************************************/
static void CALLBACK woutcallback(HANDLE wout, UINT msg, DWORD user, DWORD dw1, DWORD dw2)
{
	if (msg == WOM_DONE)
		This.m_nWrote = (This.m_nWrote==0)?0:This.m_nWrote-1; 
}

/***********************************
   Initialize sound
 ***********************************/
int CESound_Init(int SRate, int SBit, int Channels, int BufSize, int BufCount )
{
    MMRESULT res;
    WAVEFORMATEX wf;
    int i;

    This.m_hWaveOut = NULL;
    This.m_nStatus  = CESOUND_INIT;

    if( !systemSoundOn ) return 0; 

    This.m_nSampleRate = SRate;
    This.m_nSampleBits = SBit;
    This.m_nChannels   = Channels;
    This.m_nBufSize    = BufSize;
    This.m_nBufCount   = BufCount;

    wf.wFormatTag      = WAVE_FORMAT_PCM;
    wf.nChannels       = This.m_nChannels; 
    wf.nSamplesPerSec  = This.m_nSampleRate; 
    wf.nBlockAlign     = This.m_nChannels   * This.m_nSampleBits / 8;
    wf.nAvgBytesPerSec = This.m_nSampleRate * wf.nBlockAlign; 
    wf.wBitsPerSample  = This.m_nSampleBits; 
    wf.cbSize = 0;

    Sleep(500);

    res = waveOutOpen(
        &This.m_hWaveOut,   // Handle
        0,                  // ID (0 for wave mapper)
        &wf,                // Wave format
        0,                  // Callback
        (DWORD)NULL,                    //   (DWORD)woutcallback,// Instance data
        CALLBACK_NULL);                 // CALLBACK_FUNCTION

    if (res != MMSYSERR_NOERROR){
        LOG("waveOutOpen error [%d]\n", res);
        return 0;
    }

    // Wave header allocate
    This.m_WaveHdrs = (WAVEHDR *) malloc( sizeof(WAVEHDR) * This.m_nBufCount );

    // Wave buffer allocate and prepare
    for( i=0;i< This.m_nBufCount ;i++ ){
        This.m_WaveHdrs[i].lpData          = (LPSTR)malloc(This.m_nBufSize );
		This.m_WaveHdrs[i].dwBufferLength  = This.m_nBufSize;
		This.m_WaveHdrs[i].dwBytesRecorded = 0;
		This.m_WaveHdrs[i].dwUser          = 0;
		This.m_WaveHdrs[i].dwFlags         = 0;
		This.m_WaveHdrs[i].dwLoops         = 0;
		This.m_WaveHdrs[i].lpNext          = NULL;
		This.m_WaveHdrs[i].reserved        = 0;
        waveOutPrepareHeader(This.m_hWaveOut, &This.m_WaveHdrs[i], sizeof(WAVEHDR));
    }       

    This.m_nStatus = CESOUND_PLAY;

    LOG("waveOutOpen OK\n");
    return 1;
}

/***********************************
   write sound
 ***********************************/
void CESound_Write(WORD*buf, int cnt)
{
    if( This.m_nStatus != CESOUND_PLAY ) return;

//    if( CESound_isBufferFull() ) return;
    
    memcpy( This.m_WaveHdrs[This.m_nCurrent].lpData , buf,  cnt );

    This.m_WaveHdrs[This.m_nCurrent].dwFlags &= ~WHDR_DONE;
    waveOutWrite(This.m_hWaveOut, &This.m_WaveHdrs[This.m_nCurrent], sizeof(WAVEHDR));
    This.m_nCurrent ++;
    This.m_nCurrent = This.m_nCurrent % This.m_nBufCount;
//	This.m_nWrote ++;
}

/***********************************
   trash sound
 ***********************************/
void CESound_Trash(void)
{
    BOOL done;
    int i, ticks;

    ticks = GetTickCount();
    done = FALSE;
    while (!done && GetTickCount() - ticks < 500)
    {
        done = TRUE;
        for (i = 0; i < This.m_nBufCount; i++)
        {
            if (This.m_WaveHdrs[i].dwFlags & WHDR_DONE)
            {
                waveOutUnprepareHeader(This.m_hWaveOut, &This.m_WaveHdrs[i], sizeof(WAVEHDR));
                free( This.m_WaveHdrs[i].lpData ); 
                This.m_WaveHdrs[i].lpData = NULL;
                This.m_WaveHdrs[i].dwBufferLength = 0;
                This.m_WaveHdrs[i].dwFlags = 0;
            }
            else if (This.m_WaveHdrs[i].dwFlags)
                done = FALSE;
        }
    }
    waveOutClose(This.m_hWaveOut);
    free(This.m_WaveHdrs);
    LOG("Sound trash OK\n");
}

/***********************************
   sound buffer full
 ***********************************/
int  CESound_isBufferFull(void)
{
	return ((This.m_nCurrent==This.m_nWrote)?TRUE:FALSE);
}

/***********************************
   buffer pointer
 ***********************************/
char * CESound_BufferPtr(void)
{
	return (char *)This.m_WaveHdrs[This.m_nCurrent].lpData;
}

/***********************************
   sound status
 ***********************************/
int CESound_status(void)
{
	return This.m_nStatus;
}
