/****************************************************************************************/
/*  SOUND.C                                                                             */
/*                                                                                      */
/*  Author:                                                                             */
/*  Description:                                                                        */
/*                                                                                      */
/*  The contents of this file are subject to the Jet3D Public License                   */
/*  Version 1.02 (the "License"); you may not use this file except in                   */
/*  compliance with the License. You may obtain a copy of the License at                */
/*  http://www.jet3d.com                                                                */
/*                                                                                      */
/*  Software distributed under the License is distributed on an "AS IS"                 */
/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
/*  the License for the specific language governing rights and limitations              */
/*  under the License.                                                                  */
/*                                                                                      */
/*  The Original Code is Jet3D, released December 12, 1999.                             */
/*  Copyright (C) 1996-1999 Eclipse Entertainment, L.L.C. All Rights Reserved           */
/*                                                                                      */
/****************************************************************************************/
#include	<windows.h>
#include	<dsound.h>
#include	<stdio.h>
#include	<assert.h>

#include	"BaseType.h"
#include	"ErrorLog.h"
#include	"VFile.h"
#include	"Sound.h"
#include	"Mp3Mgr.h" //cyrius
#include	"Ram.h"

typedef struct	SoundManager	SoundManager;
typedef struct  Channel			Channel;


typedef struct jeSound_System
{
	jeBoolean		Active;
	SoundManager	*SoundM;
	jeFloat			GlobalVolume;
	jeMp3Mgr		*Mp3M;
} jeSound_System;

typedef struct jeSound_Cfg
{
	jeFloat			Volume;
	jeFloat			Pan;
	jeFloat			Frequency;
} jeSound_Cfg;


/*
	The interfaces here allow an application to write sound data to
	abstract channels which are then to be mixed.  The interfaces here
	require two things.  First, that the application create only one
	sound manager per instance, and second that the type of sound data
	being passed into the sound channels remains constant.  That is,
	the format of the binary information is all one format from
	one sound to another; the application cannot combine RIFF and WAV
	formats in a single channel.
*/
/*
	Call these ones only once per application:
*/

static SoundManager *	CreateSoundManager(HWND hWnd);
static void		DestroySoundManager(SoundManager *sm);

static BOOL		jeSound_FillSoundChannel(SoundManager *sm, jeVFile *File, unsigned int* Handle );
static BOOL		jeSound_StartSoundChannel( SoundManager *sm, unsigned int Handle, jeSound_Cfg *cfg, int loop, unsigned int* sfx);
static BOOL		jeSound_StopSoundChannel(Channel *channel);
static BOOL		jeSound_FreeAllChannels(SoundManager *sm);
static BOOL		jeSound_FreeChannel(SoundManager *sm, Channel *channel);
static BOOL		jeSound_ModifyChannel( Channel *channel, jeSound_Cfg *cfg );
static int		jeSound_ChannelPlaying( Channel *channel );
static Channel*	jeSound_GetChannel( SoundManager *sm, unsigned int ID );

jeBoolean		OpenMediaFile(LPSTR szFile );
void			DeleteContentsMp3();
void			PlayMp3(long volume, jeBoolean loop); 
void			StopMp3();
jeBoolean		Mp3Playing();


typedef struct Channel
{
	LPDIRECTSOUNDBUFFER	buffer;
	unsigned int		ID;
	int					BaseFreq;
	jeSound_Cfg			cfg;
	void *				Data;
	struct Channel		*next;
	struct Channel		*nextDup;
} Channel;

typedef struct	SoundManager
{
	int						smChannelCount;
	unsigned int			smNextChannelID;

	LPDIRECTSOUNDBUFFER 	smPrimaryChannel;
	Channel*				smChannels;
}   SoundManager;


#pragma message ("move these globals into the sound system struct")
static	LPDIRECTSOUND			lpDirectSound;
static  HMODULE					hmodDirectSound = NULL;

//=====================================================================================
//	jeSound_SystemCreate
//=====================================================================================
JETAPI	jeSound_System *jeSound_CreateSoundSystem(HWND hWnd)
{
	jeSound_System		*SoundSystem;

	SoundSystem = JE_RAM_ALLOCATE_STRUCT(jeSound_System);

	if (!SoundSystem)
	{
		jeErrorLog_Add(JE_ERR_MEMORY_RESOURCE, "jeSound_CreateSoundSystem.");
		return NULL;
	}

	memset(SoundSystem, 0, sizeof(jeSound_System));
	
	// Initialize the sound system
	SoundSystem->SoundM = CreateSoundManager(hWnd);

	if (!SoundSystem->SoundM)
	{
		jeRam_Free(SoundSystem);
		jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE, "jeSound_CreateSoundSystem:  Failed to create sound system.");
		return NULL;
	}
	
	//Mp3Mgr integration (CyRiuS)
	SoundSystem->Mp3M = jeMp3_CreateManager(hWnd);

	if (!SoundSystem->Mp3M)
	{
		jeRam_Free(SoundSystem);
		jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE, "jeSound_CreateSoundSystem:  Failed to create mp3 manager.");
		return NULL;
	}

	SoundSystem->GlobalVolume = 1.0f;

	return SoundSystem;
}

//=====================================================================================
//	jeSound_SetHwnd
//=====================================================================================
JETAPI	jeBoolean jeSound_SetHwnd(HWND hWnd)
{
	HRESULT Res;
	if (lpDirectSound)
		{
			#pragma message ("uses global, and doesn't assert if it's bad.")
			assert( lpDirectSound );
			Res = IDirectSound_SetCooperativeLevel(lpDirectSound, hWnd,DSSCL_NORMAL);
			if (Res != DS_OK)
				{
					jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"jeSound_SetHwnd:  IDirectSound_SetCooperativeLevel failed.");
					return JE_FALSE;
				}
		}
	return JE_TRUE;
}

//=====================================================================================
//	jeSound_SystemFree
//=====================================================================================
JETAPI	void jeSound_DestroySoundSystem(jeSound_System *Sound)
{
	assert(Sound != NULL);

	// Shutdown the sound system
	jeMp3_DestroyManager(&Sound->Mp3M); //cyrius
	DestroySoundManager(Sound->SoundM);

	Sound->SoundM = NULL;

	jeRam_Free(Sound);
}

//=====================================================================================
//	Sound_LoadSound
//=====================================================================================
//JETAPI	jeSound_Def *jeSound_LoadSoundDef(jeSound_System *SoundS, const char *Path, const char *FileName)
JETAPI	jeSound_Def *jeSound_LoadSoundDef(jeSound_System *SoundS, jeVFile *File)
{
	unsigned int SoundDef = 0;

	assert(SoundS != NULL);

	if (!jeSound_FillSoundChannel(SoundS->SoundM, File, &SoundDef))
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_LoadSoundDef.");
			return NULL;
		}
	
	return (jeSound_Def *)SoundDef;
}

//=====================================================================================
//	Mp3_LoadSound
//=====================================================================================
JETAPI int jeMp3_LoadSound(jeSound_System *SoundS, char * filename, int ref)
{
	assert(SoundS != NULL);

	SoundS->Mp3M->num_mp3s++;
	SoundS->Mp3M->cur_mp3 = ref;
	SoundS->Mp3M->files[ref].szFileName = filename;
	
	return MP3_LOAD_SUCCESS;
}


//=====================================================================================
//	Sound_FreeSound
//=====================================================================================
JETAPI	jeBoolean jeSound_FreeSoundDef(jeSound_System *SoundS, jeSound_Def *SoundDef)
{
	Channel*	Channel;

	assert(SoundS != NULL);
	assert(SoundDef != 0);

	Channel = jeSound_GetChannel(SoundS->SoundM, (unsigned int)SoundDef);

	if (!Channel)
		{
			jeErrorLog_Add(JE_ERR_SEARCH_FAILURE,"jeSound_FreeSoundDef:  Sound not found.");
			return JE_FALSE;
		}

	if (!jeSound_FreeChannel(SoundS->SoundM, Channel))
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_FreeSoundDef.");
			return JE_FALSE;
		}
	return JE_TRUE;
}

//=====================================================================================
//	Sound_SetGlobalVolume
//=====================================================================================
JETAPI	jeBoolean jeSound_SetMasterVolume( jeSound_System *SoundS, jeFloat Volume )
{
	assert ( SoundS );
	SoundS->GlobalVolume = Volume;
	return( JE_TRUE );
}
	
//=====================================================================================
//	Sound_PlaySound
//=====================================================================================
JETAPI	jeSound *jeSound_PlaySoundDef(jeSound_System *SoundS, 
							jeSound_Def *SoundDef, 
							jeFloat Volume, 
							jeFloat Pan, 
							jeFloat Frequency, 
							jeBoolean Loop)
{
	unsigned int Sound;
	jeSound_Cfg LocalCfg;

	LocalCfg.Volume		= Volume;
	LocalCfg.Pan		= Pan;
	LocalCfg.Frequency  = Frequency;

	LocalCfg.Volume *= SoundS->GlobalVolume;
	if (!jeSound_StartSoundChannel(SoundS->SoundM, (unsigned int)SoundDef, &LocalCfg, (BOOL)Loop, &Sound))
	{
		jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_PlaySoundDef.");
		return NULL;
	}

	return (jeSound *)Sound;
}

//=====================================================================================
//	Mp3_PlaySound
//=====================================================================================
JETAPI	int jeMp3_PlaySound(jeSound_System *SoundS, int song_number, long Volume, jeBoolean Loop)
{
	assert(SoundS != NULL);
	
	if(!OpenMediaFile(SoundS->Mp3M->files[song_number].szFileName))
	{
		jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE, "jeMp3_PlaySound: cant load sound from disk");
		return MP3_LOAD_FAIL;
	}

	PlayMp3(Volume, Loop);

	return MP3_LOAD_SUCCESS;
}
	
//=====================================================================================
//	Sound_StopSound
//=====================================================================================
JETAPI	jeBoolean jeSound_StopSound(jeSound_System *SoundS, jeSound *Sound)
{
	Channel*	Channel;

	assert(SoundS != NULL);
	assert(Sound  != NULL);	

	Channel = jeSound_GetChannel(SoundS->SoundM, (unsigned int)Sound);

	if (!Channel)
		{
			jeErrorLog_Add(JE_ERR_SEARCH_FAILURE,"jeSound_StopSound:  Sound not playing.");
			return JE_FALSE;
		}

	if (jeSound_StopSoundChannel(Channel)==JE_FALSE)
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_StopSound:  Sound failed to stop.");
			return JE_FALSE;
		}
	return JE_TRUE;	
}

//=====================================================================================
//	Sound_ModifySound
//=====================================================================================
JETAPI	jeBoolean jeSound_ModifySound(jeSound_System *SoundS, 
								jeSound *Sound,jeFloat Volume, 
								jeFloat Pan, 
								jeFloat Frequency)
{
	Channel*	Channel;
	jeSound_Cfg	LocalCfg;

	assert(SoundS != NULL);
	assert(Sound  != NULL);	

	Channel = jeSound_GetChannel(SoundS->SoundM, (unsigned int)Sound);

	if (!Channel)
		{
			jeErrorLog_Add(JE_ERR_SEARCH_FAILURE,"jeSound_ModifySound:  Sound not found.");
			return JE_FALSE;
		}

	LocalCfg.Volume    = Volume;
	LocalCfg.Pan       = Pan;
	LocalCfg.Frequency = Frequency;
	LocalCfg.Volume *= SoundS->GlobalVolume;
	if ( jeSound_ModifyChannel(Channel, &LocalCfg) == JE_FALSE)
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_ModifySound:  Failed to modify channel.");
			return JE_FALSE;
		}
	return JE_TRUE;
}

//=====================================================================================
//	Sound_SoundIsPlaying
//=====================================================================================
JETAPI	jeBoolean jeSound_SoundIsPlaying(jeSound_System *SoundS, jeSound *Sound)
{
	Channel*	Channel;

	assert(SoundS != NULL);
	assert(Sound  != NULL);	

	Channel = jeSound_GetChannel(SoundS->SoundM, (unsigned int)Sound);

	if (!Channel)
		{
			return JE_FALSE;
		}

	return jeSound_ChannelPlaying(Channel);
}


//=====================================================================================
//=====================================================================================

static	BOOL DSParseWaveResource(const void *pvRes, WAVEFORMATEX **ppWaveHeader,
                         BYTE **ppbWaveData,DWORD *pcbWaveSize)
{
    DWORD *pdw;
    DWORD *pdwEnd;
    DWORD dwRiff;
    DWORD dwType;
    DWORD dwLength;

    if (ppWaveHeader)
        *ppWaveHeader = NULL;

    if (ppbWaveData)
        *ppbWaveData = NULL;

    if (pcbWaveSize)
        *pcbWaveSize = 0;

    pdw = (DWORD *)pvRes;
    dwRiff = *pdw++;
    dwLength = *pdw++;
    dwType = *pdw++;

    if (dwRiff != mmioFOURCC('R', 'I', 'F', 'F'))
        {
			jeErrorLog_Add(JE_ERR_BAD_PARAMETER,"DSParseWaveResource: not RIFF format.");
			goto exit;      // not even RIFF
		}

    if (dwType != mmioFOURCC('W', 'A', 'V', 'E'))
        {
			jeErrorLog_Add(JE_ERR_BAD_PARAMETER,"DSParseWaveResource: not WAVE format.");
			goto exit;      // not a WAV
		}

    pdwEnd = (DWORD *)((BYTE *)pdw + dwLength-4);

    while (pdw < pdwEnd)
    {
        dwType = *pdw++;
        dwLength = *pdw++;

        switch (dwType)
        {
        case mmioFOURCC('f', 'm', 't', ' '):
            if (ppWaveHeader && !*ppWaveHeader)
            {
                if (dwLength < sizeof(WAVEFORMAT))
                    {
						jeErrorLog_Add(JE_ERR_BAD_PARAMETER,"DSParseWaveResource: not proper WAV format.");
						goto exit;      // not a WAV
					}

                *ppWaveHeader = (WAVEFORMATEX *)pdw;

                if ((!ppbWaveData || *ppbWaveData) &&
                    (!pcbWaveSize || *pcbWaveSize))
                {
                    return TRUE;
                }
            }
            break;

        case mmioFOURCC('d', 'a', 't', 'a'):
            if ((ppbWaveData && !*ppbWaveData) ||
                (pcbWaveSize && !*pcbWaveSize))
            {
                if (ppbWaveData)
                    *ppbWaveData = (LPBYTE)pdw;

                if (pcbWaveSize)
                    *pcbWaveSize = dwLength;

                if (!ppWaveHeader || *ppWaveHeader)
                    return TRUE;
            }
            break;
        }

        pdw = (DWORD *)((BYTE *)pdw + ((dwLength+1)&~1));
    }

exit:
    return FALSE;
}

static	BOOL DSFillSoundBuffer(IDirectSoundBuffer *pDSB, BYTE *pbWaveData, DWORD cbWaveSize)
{
	assert( pDSB );
	assert( pbWaveData );
	assert( cbWaveSize );

    {
        LPVOID pMem1, pMem2;
        DWORD dwSize1, dwSize2;

        if (SUCCEEDED(IDirectSoundBuffer_Lock(pDSB, 0, cbWaveSize,
            &pMem1, &dwSize1, &pMem2, &dwSize2, 0)))
        {
            ZeroMemory(pMem1, dwSize1);
            CopyMemory(pMem1, pbWaveData, dwSize1);

            if ( 0 != dwSize2 )
                CopyMemory(pMem2, pbWaveData+dwSize1, dwSize2);

            IDirectSoundBuffer_Unlock(pDSB, pMem1, dwSize1, pMem2, dwSize2);
            return TRUE;
        }
		else
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"DSFillSoundBuffer: IDirectSoundBuffer_Lock failed.");
			return FALSE;
		}
    }
}


DSCAPS			dsCaps;
static	SoundManager *	CreateSoundManager(HWND hWnd )
{
	typedef HRESULT (WINAPI *DS_CREATE_FUNC)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
	PCMWAVEFORMAT	pcmwf;
	DSBUFFERDESC	dsbdesc;
	HRESULT			hres;
	SoundManager *	sm;
	DS_CREATE_FUNC pDirectSoundCreate;

	// load the DirectSound DLL
	hmodDirectSound = LoadLibrary ("DSOUND.DLL");
	if (hmodDirectSound == NULL)
	{
		// Couldn't load DSOUND.DLL
		jeErrorLog_Add(JE_ERR_SYSTEM_RESOURCE,"CreateSoundManager: Could not load DSOUND.DLL.");
		return NULL;
	}

	pDirectSoundCreate = (DS_CREATE_FUNC)GetProcAddress (hmodDirectSound, "DirectSoundCreate");
	if (pDirectSoundCreate == NULL)
	{
		// couldn't find the DirectSoundCreate function
		jeErrorLog_Add(JE_ERR_SYSTEM_RESOURCE,"CreateSoundManager: Could not find DirectSoundCreate entry in DSOUND.DLL.");
		FreeLibrary (hmodDirectSound);
		return NULL;
	}

	hres = pDirectSoundCreate(NULL, &lpDirectSound, NULL);
	if	(hres != DS_OK)
	{
		// failed somehow
		jeErrorLog_Add(JE_ERR_SOUND_RESOURCE,"CreateSoundManager: Could not initialize Direct Sound.");
		FreeLibrary (hmodDirectSound);
		return NULL;
	}

	sm = jeRam_Allocate(sizeof(*sm));
	if	(!sm)
	{
		jeErrorLog_Add(JE_ERR_MEMORY_RESOURCE,"CreateSoundManager.");
		IDirectSound_Release(lpDirectSound);
		FreeLibrary (hmodDirectSound);
		return NULL;
	}
	sm->smChannelCount = 0;
	sm->smNextChannelID = 1;
	sm->smChannels = NULL;

	memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
	pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM;

	//pcmwf.wf.nChannels = 1;
	//pcmwf.wf.nSamplesPerSec = 44050;
	//pcmwf.wf.nBlockAlign = 2;
#if 1	
	pcmwf.wf.nChannels = 2;
	pcmwf.wf.nSamplesPerSec = 44100;
	pcmwf.wf.nBlockAlign = 4;
	pcmwf.wBitsPerSample = 16;
	pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign;
#else
	pcmwf.wf.nChannels = 1;
	pcmwf.wf.nSamplesPerSec = 22050;
	pcmwf.wf.nBlockAlign = 1;
	pcmwf.wBitsPerSample = 8;
	pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * 2;
#endif

	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;// | DSBCAPS_CTRLDEFAULT;// | DSBCAPS_CTRL3D;
	dsbdesc.dwBufferBytes = 0; //dwBufferBytes and lpwfxFormat must be set this way.
	dsbdesc.lpwfxFormat = NULL;

#if 1
	if (DS_OK== IDirectSound_SetCooperativeLevel(lpDirectSound, hWnd,DSSCL_NORMAL))
#else
	if (DS_OK== IDirectSound_SetCooperativeLevel(lpDirectSound, hWnd,DSSCL_EXCLUSIVE))
#endif
	{
		if (DS_OK== IDirectSound_CreateSoundBuffer(lpDirectSound, &dsbdesc, &sm->smPrimaryChannel, NULL))
		{
			return sm;
		}
		jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"CreateSoundManager: IDirectSound_CreateSoundBuffer failed.");
		IDirectSound_Release(lpDirectSound);
		FreeLibrary (hmodDirectSound);
	}
	jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"CreateSoundManager: IDirectSound_SetCooperativeLevel failed.");
	
	jeRam_Free(sm);
	return NULL;
}

static	BOOL CreateChannel(DSBUFFERDESC*	dsBD, Channel** chanelPtr)
{
	Channel* channel;

	channel = jeRam_Allocate( sizeof( Channel ) );
	if	( channel == NULL )
	{
		jeErrorLog_Add(JE_ERR_MEMORY_RESOURCE, "CreateChannel.");
		return( FALSE );
	}
	if(DS_OK != IDirectSound_CreateSoundBuffer(lpDirectSound, dsBD, &channel->buffer, NULL))
	{
		jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE, "CreateChannel: IDirectSound_CreateSoundBuffer failed.");
		return FALSE;
	}
	if(DS_OK != IDirectSoundBuffer_GetFrequency(channel->buffer, &channel->BaseFreq) )
	{
		jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE, "CreateChannel: IDirectSound_GetFrequency failed.");
		return FALSE;
	}
	channel->next = NULL;
	channel->nextDup = NULL;
	channel->ID = 0;
	channel->cfg.Volume = 1.0f;
	channel->cfg.Pan = 0.0f;
	channel->cfg.Frequency = 0.0f;
//	channel->name = Name;

	*chanelPtr = channel;
	return( TRUE );
}

//static	BOOL GetSoundData( char* Name, unsigned char** dataPtr)
static	BOOL GetSoundData( jeVFile *File, unsigned char** dataPtr)
{
//	FILE * f;
	int32 Size;
	uint8 *data;
//	int32		CurPos;

#if 0
	f = fopen(Name, "rb");
	
	if (!f)
	{
		jeErrorLog_Add(JE_ERR_FILEIO_OPEN, "GetSoundData.");
		return FALSE;
	}
#endif

#if 0
	CurPos = ftell (f);				// Save the startinf pos into this function
	fseek (f, 0, SEEK_END);			// Seek to end
	Size = ftell (f);				// Get End (this will be the size)
	fseek (f, CurPos, SEEK_SET);	// Restore file position
#endif

	if	(jeVFile_Size(File, &Size) == JE_FALSE)
		{
			jeErrorLog_Add(JE_ERR_FILEIO_READ,"GetSoundData: failed to get size of sound file.");
			return FALSE;
		}

	data = jeRam_Allocate(Size);

	if (!data) 
	{
		jeErrorLog_Add(JE_ERR_MEMORY_RESOURCE, "GetSoundData.");
		return FALSE;
	}
	
	if	(jeVFile_Read(File, data, Size) == JE_FALSE)
	{
		jeErrorLog_Add(JE_ERR_FILEIO_READ,"GetSoundData: failed to read sound data.");
		jeRam_Free(data);
		return FALSE;
	}

//	fread(data, Size, 1, f);

//	fclose(f);
	*dataPtr = data;
	return( TRUE );
}

static	BOOL ParseData( const uint8* data, DSBUFFERDESC* dsBD, BYTE ** pbWaveData )
{

	//Parse the Data
	memset(dsBD, 0, sizeof(DSBUFFERDESC));

	dsBD->dwSize = sizeof(DSBUFFERDESC);
	dsBD->dwFlags = DSBCAPS_STATIC | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME ;// | DSBCAPS_CTRLDEFAULT;
	if	(!DSParseWaveResource(data, &dsBD->lpwfxFormat, pbWaveData, &dsBD->dwBufferBytes))
	{
		jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE, "ParseData.");
		return FALSE;
	}
	return( TRUE );

}

static	BOOL jeSound_FillSoundChannel(SoundManager *sm, jeVFile *File, unsigned int* Handle )
{
	DSBUFFERDESC	dsBD;
	INT NumBytes;
	uint8		*data = NULL;
	BYTE *			pbWaveData;
	Channel* channel;

	assert( Handle );
	assert( sm );

	*Handle = 0;
	
	if(!GetSoundData( File, &data ))
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_FillSoundChannel.");
			return( FALSE );
		}

	if( !ParseData( data, &dsBD, &pbWaveData ) )
	{
		jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_FillSoundChannel.");
		jeRam_Free(data);
		return( FALSE );
	}

	NumBytes = dsBD.dwBufferBytes;
	
	//Create the channel
	if	(!CreateChannel(&dsBD, &channel))
	{
		jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_FillSoundChannel.");
		jeRam_Free(data);
		return FALSE;
	}
	channel->next = sm->smChannels;
	channel->ID = sm->smNextChannelID++;
	channel->Data = data;

	sm->smChannels = channel;
	sm->smChannelCount++;

	//Fill the channel
	if (!DSFillSoundBuffer(channel->buffer, pbWaveData, NumBytes))
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_FillSoundChannel.");
			return FALSE;
		}
	
	*Handle = channel->ID;
	return TRUE;
}


static	void StopDupBuffers( Channel* channel )
{
	Channel* dupChannel, *prevChannel;

	assert( channel );

	dupChannel = channel->nextDup;
	prevChannel = channel;
	while( dupChannel )
	{
		IDirectSoundBuffer_Stop(dupChannel->buffer);
		dupChannel = dupChannel->nextDup;
	}
}

static	void ClearDupBuffers( Channel* channel )
{
	Channel* dupChannel, *prevChannel;
	assert( channel );

	dupChannel = channel->nextDup;
	prevChannel = channel;
	while( dupChannel )
	{
		if( !jeSound_ChannelPlaying( dupChannel ) )
		{
			prevChannel->nextDup = dupChannel->nextDup;
			IDirectSound_Release(dupChannel->buffer);
//			free( dupChannel );
			jeRam_Free(dupChannel);
			dupChannel = prevChannel->nextDup;
		}
		else
		{
			prevChannel = dupChannel;
			dupChannel = dupChannel->nextDup;
		}
	}
}

static	BOOL jeSound_FreeAllChannels(SoundManager *sm)
{
	int Error;
	
	Channel* channel, *nextChannel;

	channel = sm->smChannels;
	while( channel )
	{
		nextChannel = channel->next;
		StopDupBuffers( channel );
		ClearDupBuffers( channel );
		Error = IDirectSoundBuffer_Stop(channel->buffer);
		if (Error != DS_OK)
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE, "jeSound_FreeAllChannels: IDirectSoundBuffer_Stop failed.");
			return FALSE;
		}
		Error = IDirectSound_Release(channel->buffer);
		if (Error != DS_OK)
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE, "jeSound_FreeAllChannels: IDirectSound_Release failed.");
			return FALSE;
		}
		
		if	(channel->Data)
			jeRam_Free(channel->Data);
		jeRam_Free(channel);
		channel = nextChannel;
	}
	sm->smChannels = NULL;
	sm->smChannelCount = 0;

	return TRUE;
}


static	BOOL jeSound_FreeChannel(SoundManager *sm, Channel* channel)
{
	int Error;
	Channel*prevChannel = NULL, *curChannel;
	assert( channel );
	assert( sm );

	{
		StopDupBuffers( channel );
		ClearDupBuffers( channel );
		Error = IDirectSoundBuffer_Stop(channel->buffer);
		if (Error != DS_OK)
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE, "jeSound_FreeChannel: IDirectSoundBuffer_Stop failed.");
			return FALSE;
		}
		Error = IDirectSound_Release(channel->buffer);
		if (Error != DS_OK)
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE, "jeSound_FreeChannel: IDirectSound_Release failed.");
			return FALSE;
		}

		if( channel->Data )
			jeRam_Free(channel->Data);

		curChannel = sm->smChannels;
		while( curChannel && curChannel != channel )
		{
			prevChannel = curChannel;
			curChannel = curChannel->next;
		}
		if( curChannel )
		{
			if( prevChannel )
				prevChannel->next = curChannel->next;
			else
				sm->smChannels = curChannel->next;
			jeRam_Free(curChannel);
		}
	}

	return TRUE;
}

static	Channel* ReloadData(void *Data)
{
	DSBUFFERDESC	dsBD;
	BYTE *			pbWaveData;
	INT NumBytes;
	Channel* channel;

	if( !ParseData( Data, &dsBD, &pbWaveData ) )
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"ReloadData");
			return( NULL );
		}

	NumBytes = dsBD.dwBufferBytes;
	
	//Create the channel
	if( !CreateChannel(&dsBD, &channel ) )
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"ReloadData");
			return NULL;
		}

	//Fill the channel
	if ( !DSFillSoundBuffer(channel->buffer, pbWaveData, NumBytes))
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"ReloadData");
			return NULL;
		}
	return( channel );
}

static	BOOL DupChannel( SoundManager *sm, Channel* channel, Channel** dupChannelPtr )
{
	Channel* dupChannel;
	HRESULT Error;

	assert( sm );
	assert( channel );
	assert( dupChannelPtr );

	*dupChannelPtr = NULL;
	dupChannel =  jeRam_Allocate( sizeof(Channel ) );
	if( dupChannel == NULL )
	{
		jeErrorLog_Add(JE_ERR_MEMORY_RESOURCE, "DupChannel" );
		return FALSE;
	}
	Error = IDirectSound_DuplicateSoundBuffer( lpDirectSound, channel->buffer, &dupChannel->buffer );
	if( Error != DS_OK )
	{
		jeRam_Free(dupChannel);
		dupChannel = ReloadData( channel->Data );
		if( dupChannel == NULL )
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE, "DupChannel");
			return FALSE;
		}
	}
	dupChannel->ID =  sm->smNextChannelID++;
	dupChannel->next = NULL;
	dupChannel->nextDup = channel->nextDup;
	dupChannel->cfg = channel->cfg;
	dupChannel->Data = channel->Data;
	channel->nextDup = dupChannel;
	*dupChannelPtr = dupChannel;
	return( TRUE );
}

static	BOOL	jeSound_StartSoundChannel( SoundManager *sm, unsigned int Handle, jeSound_Cfg *cfg, int loop, unsigned int* sfx)
{
	HRESULT	hres;
	Channel* channel, *dupChannel;
	
	assert( sm );
	assert( cfg );

	if( Handle == 0 )
		{
			jeErrorLog_Add(JE_ERR_BAD_PARAMETER,"jeSound_StartSoundChannel: bad handle (0).");
			return( FALSE );
		}
	channel = jeSound_GetChannel( sm, Handle );
	//Clear all non-playing duplicate buffers.
	if (!channel)
		{
			jeErrorLog_Add(JE_ERR_INTERNAL_RESOURCE,"jeSound_StartSoundChannel: no channel available.");
			return ( FALSE );
		}
	ClearDupBuffers(channel);
	//If the main buffer is playing and all non-playing dups have been cleared
	//we need a new duplicate.
	if( jeSound_ChannelPlaying( channel ) )
	{
		if(!DupChannel( sm,channel, &dupChannel ) )
			{
				jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_StartSoundChannel.");
				return( FALSE );
			}
		channel = dupChannel;
	}
	if( !jeSound_ModifyChannel( channel, cfg ) )
		{
			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"jeSound_StartSoundChannel.");
			return( FALSE );
		}
	IDirectSoundBuffer_SetCurrentPosition(channel->buffer, 0);
	hres = IDirectSoundBuffer_Play( channel->buffer,
				  				   0,
				  				   0,
				  				   loop ? DSBPLAY_LOOPING : 0);

	if	(hres == DS_OK)
	{
		if( sfx )
			*sfx = channel->ID;
		return TRUE;
	}
	
	jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"jeSound_StartSoundChannel: IDirectSoundBuffer_Play failed.");
	return FALSE;
}

static	BOOL jeSound_StopSoundChannel(Channel* channel)
{
	HRESULT	hres;

	assert(channel);

	hres = IDirectSoundBuffer_Stop(channel->buffer);

	if	(hres == DS_OK)
		return TRUE;

	jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"jeSound_StopSoundChannel: IDirectSoundBuffer_Stop failed.");
	return FALSE;
}

static	void DestroySoundManager(SoundManager *sm)
{
	assert( sm );

	jeSound_FreeAllChannels( sm );
	if	(sm->smPrimaryChannel != NULL)
		sm->smPrimaryChannel->lpVtbl->Release(sm->smPrimaryChannel);
	if	(lpDirectSound != NULL)
		IDirectSound_Release(lpDirectSound);
	if  (hmodDirectSound != NULL)
		FreeLibrary (hmodDirectSound);
	jeRam_Free(sm);
}

static	BOOL	jeSound_ModifyChannel( Channel *channel, jeSound_Cfg *cfg )
{
	int Error, Vol, Pan, Freq;
	assert( channel );
	assert( cfg     );	

	ClearDupBuffers(channel);
	if( cfg->Volume != channel->cfg.Volume )
	{
		Vol = (DWORD)((1.0 - cfg->Volume  ) * DSBVOLUME_MIN);
		Error = IDirectSoundBuffer_SetVolume(channel->buffer, Vol);
		if (Error != DS_OK)
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"jeSound_ModifyChannel: IDirectSoundBuffer_SetVolume failed.");
			return FALSE;
		}
		channel->cfg.Volume = cfg->Volume;
	}

	if( cfg->Pan != channel->cfg.Pan )
	{
		Pan = (int)(cfg->Pan  * DSBPAN_RIGHT);
		Error = IDirectSoundBuffer_SetPan(channel->buffer, Pan);
		if (Error != DS_OK)
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"jeSound_ModifyChannel: IDirectSoundBuffer_SetVolume failed.");
			return FALSE;
		}
		channel->cfg.Pan = cfg->Pan;
	}


	if( cfg->Frequency != channel->cfg.Frequency )
	{

		Freq = (DWORD)(channel->BaseFreq * cfg->Frequency);
		Error = IDirectSoundBuffer_SetFrequency(channel->buffer, Freq);
		if (Error != DS_OK)
		{
			jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"jeSound_ModifyChannel: IDirectSoundBuffer_SetFrequency failed.");
			return FALSE;
		}
		channel->cfg.Frequency = cfg->Frequency;
	}

	return TRUE;
}

static	int	jeSound_ChannelPlaying( Channel *channel )
{
	DWORD status, Error;

	assert( channel );

	Error = IDirectSoundBuffer_GetStatus( channel->buffer, &status);
	if( Error != DS_OK)
		{
			//jeErrorLog_Add(JE_ERR_WINDOWS_API_FAILURE,"jeSound_ModifyChannel: IDirectSoundBuffer_GetStatus failed.");
			return 0;
		}
	return( status & DSBSTATUS_PLAYING  );
}

static	Channel* jeSound_GetChannel( SoundManager *sm, unsigned int ID )
{
	Channel* dupChannel;
	Channel* channel = sm->smChannels;

	while( channel )
	{
		if( channel->ID == ID )
			break;
		dupChannel = channel->nextDup;
		while( dupChannel )
		{
			if( dupChannel->ID == ID )
				break;
			dupChannel = dupChannel->nextDup;
		}
		if( dupChannel )
			return( dupChannel );
		channel = channel->next;
	}
	return( channel );
}
