#include "pch.hpp"
//===========================================================================//
// File:	 Sound Resource.cpp												 //
// Project:  GameOS					                                         //
// Contents: Direct Sound manager											 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 01/23/98  JTR    Created													 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Sound Renderer.hpp"
#include "ACM.hpp"
#include "ErrorHandler.hpp"
#include "Debugger.hpp"
#include "FileIO.hpp"
#include "MemoryManager.hpp"


extern SoundRendererData SoundRenderer;
extern LinkedList<SoundResource *> *
	g_soundResources;
extern CRITICAL_SECTION 
	g_soundCritSec;

extern DWORD gLanguageDLL;

void SetupStreamedWAV(SoundResource *);

void __stdcall gosAudio_CreateResource( HGOSAUDIO * hgosaudio, const char * identifier_name, HGOSFILE file, DWORD offset, bool only2D)
{
	LOG_BLOCK("gosAudio::CreateResource");
	if (*hgosaudio != NULL)
	{
		SPEW(( GROUP_DIRECTSOUND, "gosAudio_CreateResource expects a pointer to a NULL" ));
	}

	if (!file)
		STOP((GROUP_DIRECTSOUND, "Bad parameters on a call to create a streamed sound from an open file pointer"));
	else
	{
		*hgosaudio = 
			(HGOSAUDIO) 
			new(SoundHeap) SoundResource
			(
				identifier_name,
				file,
				offset,
				only2D
			);
	}
#ifdef LAB_ONLY
#ifndef _DEBUG
	if (DebuggerActive && DebugDisplay == DbgS_PlayHistory)
#endif
	{
		char tmp[256];
		sprintf(tmp, "%9.2f    Create              n/a    <%s>\n",  gos_GetElapsedTime(), ((HGOSAUDIO)*hgosaudio)->m_FileName ? ((HGOSAUDIO)*hgosaudio)->m_FileName : "Unlabeled UserMemory");
		strncpy( SoundRenderer.m_playHistory[SoundRenderer.m_playHistoryItr], tmp, 127 );
		SPEW ((GROUP_DIRECTSOUND,"Create StreamedFromFP Sound Resource %s: (0x%x)", ((HGOSAUDIO)*hgosaudio)->m_FileName, *hgosaudio ));
		SoundRenderer.m_playHistoryItr++;
		if (SoundRenderer.m_playHistoryItr == 32)
			SoundRenderer.m_playHistoryItr = 0;
	}
#endif
}


void __stdcall gosAudio_CreateResource( HGOSAUDIO * hgosaudio, enum gosAudio_ResourceType ga_rt,  const char * file_name, gosAudio_Format * ga_wf, void * data, int size, bool only2D)
{
	LOG_BLOCK("gosAudio::CreateResource");
	if (*hgosaudio != NULL)
	{
		SPEW(( GROUP_DIRECTSOUND, "gosAudio_CreateResource expects a pointer to a NULL" ));
	}

	if (ga_rt == gosAudio_UserMemory || ga_rt == gosAudio_UserMemoryDecode)
	{
		*hgosaudio = 
			(HGOSAUDIO) 
			new(SoundHeap) SoundResource
			(
				data,
				ga_wf,
				file_name,
				size,
				only2D
			);
	}
	else
	if (ga_rt == gosAudio_UserMemoryPlayList)
	{
		*hgosaudio = 
			(HGOSAUDIO)
			new(SoundHeap) SoundResource
			(
				file_name,
				(gosAudio_PlayList *) data,
				only2D
				
			);
	}
	else
	{
		// Setup a DirectShow music stream / or a MSADPCM/PCM stream
		*hgosaudio = 
			(HGOSAUDIO) 
			new(SoundHeap) SoundResource
			(
				file_name,
				ga_rt,
				only2D
			);
	}
#ifdef LAB_ONLY
#ifndef _DEBUG
	if (DebuggerActive && DebugDisplay == DbgS_PlayHistory)
#endif
	{
		char tmp[256];
		sprintf(tmp, "%9.2f    Create              n/a    <%s>\n",  gos_GetElapsedTime(), ((HGOSAUDIO)*hgosaudio)->m_FileName ? ((HGOSAUDIO)*hgosaudio)->m_FileName : "Unlabeled UserMemory");
		strncpy( SoundRenderer.m_playHistory[SoundRenderer.m_playHistoryItr], tmp, 127 );
		switch(ga_rt)
		{
		case gosAudio_StreamedFile:
			SPEW ((GROUP_DIRECTSOUND,"Create StreamedFile Sound Resource %s: (0x%x)", ((HGOSAUDIO)*hgosaudio)->m_FileName, *hgosaudio ));
			break;
		case gosAudio_UserMemory:
			SPEW ((GROUP_DIRECTSOUND,"Create UserMemory Sound Resource %s: (0x%x)", ((HGOSAUDIO)*hgosaudio)->m_FileName ? ((HGOSAUDIO)*hgosaudio)->m_FileName : "Unlabeled UserMemory", *hgosaudio));
			break;
		case gosAudio_UserMemoryDecode:
			SPEW ((GROUP_DIRECTSOUND,"Create UserMemoryDecode Sound Resource %s: (0x%x)", ((HGOSAUDIO)*hgosaudio)->m_FileName ? ((HGOSAUDIO)*hgosaudio)->m_FileName : "Unlabeled UserMemory", *hgosaudio));
			break;
		case gosAudio_CachedFile:
			SPEW ((GROUP_DIRECTSOUND,"Create CachedFile Sound Resource %s: (0x%x)", ((HGOSAUDIO)*hgosaudio)->m_FileName, *hgosaudio));
			break;
		case gosAudio_UserMemoryPlayList:
			SPEW ((GROUP_DIRECTSOUND,"Create PlayList Sound Resource %s: (0x%x)", ((HGOSAUDIO)*hgosaudio)->m_FileName ? ((HGOSAUDIO)*hgosaudio)->m_FileName : "Unlabeled PlayList", *hgosaudio));
			break;
		}
		SoundRenderer.m_playHistoryItr++;
		if (SoundRenderer.m_playHistoryItr == 32)
			SoundRenderer.m_playHistoryItr = 0;
	}
#endif
}

void __stdcall gosAudio_DestroyResource( HGOSAUDIO * resource)
{
	LOG_BLOCK("gosAudio::DestroyResource");
	if (SoundRenderer.m_isInitialized)
	{
		EnterCriticalSection(&g_soundCritSec);
		for(int i = 0; i < SoundRenderer.m_numChannels; i++)
		{
			if (SoundRenderer.m_Channel[i]->m_currentSoundResource == *resource)
			{
				if (!(SoundRenderer.m_Channel[i]->m_resourceType == gosAudio_CachedFile ||
					SoundRenderer.m_Channel[i]->m_resourceType == gosAudio_UserMemoryDecode ||
					SoundRenderer.m_Channel[i]->m_resourceType == gosAudio_UserMemory ))
				{
					//
					// once a streamed file's resource is destroyed,
					// we must stop the stream and stop the channel
					//
					// Cached/Memeory resources are allowed to continue
					// and the channel isn't cleared....
					//
					//
					gosAudio_SetChannelPlayMode( i, gosAudio_Stop );
					SoundRenderer.m_Channel[i]->ClearAndFree();
				}
				else
				{
					SoundRenderer.m_Channel[i]->m_currentSoundResource = NULL;
				}
			}
		}
	}

	LinkedListIterator<SoundResource *> itr 
		= LinkedListIterator<SoundResource *> (g_soundResources);
	itr.Head();
	SoundResource * sr;

	bool found = FALSE;
	while((sr=itr.ReadAndNext()) != NULL)
	{
	   if (sr == (SoundResource *) *resource)
	   {
		   found = TRUE;
#ifdef LAB_ONLY
#ifndef _DEBUG
			if (DebuggerActive && DebugDisplay == DbgS_PlayHistory)
#endif
			{
				char tmp[256];
				sprintf(tmp, "%9.2f    Destroy             n/a    <%s>\n",  gos_GetElapsedTime(), sr->m_FileName ? sr->m_FileName : "Unlabeled UserMemory");
				strncpy( SoundRenderer.m_playHistory[SoundRenderer.m_playHistoryItr], tmp, 127 );
				SPEW ((GROUP_DIRECTSOUND,"Destroy Sound Resource %s: (0x%x)", sr->m_FileName ? sr->m_FileName : "Unlabeled UserMemory", *resource));
				SoundRenderer.m_playHistoryItr++;
				if (SoundRenderer.m_playHistoryItr == 32)
					SoundRenderer.m_playHistoryItr = 0;
	}
#endif
		   delete sr;
		   break;
	   }
	}
	if (!found)
	{
		STOP(( "Resource not found" ));
	}
	*resource = 0;
	LeaveCriticalSection(&g_soundCritSec);
}

void __stdcall gosAudio_GetResourceInfo(HGOSAUDIO hgosaudio, gosAudio_ResourceInfo * ga_ri)
{
	LOG_BLOCK("gosAudio::GetResourceInfo");
	if (hgosaudio == NULL)
	{
		memset( ga_ri,0,sizeof( gosAudio_ResourceInfo ) );
		return;
	}
	ga_ri->lpstrPath = hgosaudio->m_FileName;
	ga_ri->eType = hgosaudio->m_Type;
	ga_ri->sFormat.wFormatTag = hgosaudio->m_WaveFormatEx->wFormatTag;    
	ga_ri->sFormat.nChannels = hgosaudio->m_WaveFormatEx->nChannels; 
    ga_ri->sFormat.nSamplesPerSec = hgosaudio->m_WaveFormatEx->nSamplesPerSec;     
    ga_ri->sFormat.wBitsPerSample = hgosaudio->m_WaveFormatEx->wBitsPerSample;     
	ga_ri->sFormat.cbSize = hgosaudio->m_WaveFormatEx->cbSize;
	ga_ri->sFormat.nBlockAlign = hgosaudio->m_WaveFormatEx->nBlockAlign; 
	ga_ri->dwSizeInBytes = ga_ri->sFormat.wFormatTag == WAVE_FORMAT_PCM ? hgosaudio->m_DataLength : hgosaudio->m_DataLength*4;
	if (hgosaudio->m_Type == gosAudio_StreamedMusic)
		ga_ri->fDuration = ((float) hgosaudio->m_hMusic->m_fDurationSec) / 10000000.0f;
	else
		ga_ri->fDuration = (float) hgosaudio->m_fDuration;
	ga_ri->sFormat.nAvgBytesPerSec = hgosaudio->m_WaveFormatEx->nAvgBytesPerSec;     
}

SoundResource::SoundResource
(
	const char * file_name,
	enum gosAudio_ResourceType rt,
	bool only2D
)
{
	gosASSERT(file_name);
	lpMasterBuffer = NULL;
	m_FileName = NULL;
	m_playList = NULL;
	m_DataPointer = NULL;
	m_FileDataPointer = NULL;
	m_FileLength = 0;
	m_DataLength = 0;
	m_Flags = 0;
	m_Type = rt;
	m_WaveFormatEx = NULL;
    m_nDataSize = 0;
    m_nBytesPlayed = 0;
	m_nBytesUncompressed = 0;
    m_fDuration = 0;
	m_microsoftPCM = TRUE;
	m_hACMStream = NULL;
	m_uncompressedData = NULL;
	m_uncompressedDataSize = 0;
	m_only2D = only2D;

	duplicateCount = 0;
	memset( lpDuplicateBuffers, 0, sizeof(lpDuplicateBuffers) );
	memset( duplicateInUse, 0, sizeof(duplicateInUse) );

	switch(rt)
	{
	case gosAudio_CachedFile:
		{
			m_FileName = (char *) gos_Malloc(strlen(file_name)+1, SoundHeap);
			strcpy(m_FileName, file_name);
			LoadFile();
			m_fDuration = (float) m_DataLength / m_WaveFormatEx->nAvgBytesPerSec;
			break;
		}
	case gosAudio_StreamedFile:
		{
			m_FileName = (char *) gos_Malloc(strlen(file_name)+1, SoundHeap);
			strcpy(m_FileName, file_name);

			// we'll be grabbing this file piecemeal...
			SetupStreamedWAV(this);
			break;
		}
	case gosAudio_StreamedMusic:
		{
			m_FileName = (char *) gos_Malloc(strlen(file_name)+1, SoundHeap);
			strcpy(m_FileName, file_name);

			gosMusic_CreateResource(&m_hMusic,m_FileName);
			m_fDuration = (float) m_hMusic->m_fDurationSec;
			m_WaveFormatEx = (WAVEFORMATEX *) gos_Malloc(sizeof(gosAudio_Format), SoundHeap);
			memset(m_WaveFormatEx, 0, sizeof(WAVEFORMATEX));
			break;
		}
	}
	g_soundResources->Add(this);
}


SoundResource::SoundResource
(
	const char * file_name,
	HGOSFILE file,
	DWORD offset,
	bool only2D
)
{
	gosASSERT(file_name);
	lpMasterBuffer = NULL;
	m_FileName = NULL;
	m_playList = NULL;
	m_DataPointer = NULL;
	m_FileDataPointer = NULL;
	m_FileLength = 0;
	m_DataLength = 0;
	m_Flags = 0;
	m_Type = gosAudio_StreamedFP;
	m_WaveFormatEx = NULL;
    m_nDataSize = 0;
    m_nBytesPlayed = 0;
	m_nBytesUncompressed = 0;
    m_fDuration = 0;
	m_microsoftPCM = TRUE;
	m_hACMStream = NULL;
	m_uncompressedData = NULL;
	m_uncompressedDataSize = 0;
	m_only2D = only2D;

	//
	// We need to duplicate the file pointer, man!
	//

	gos_OpenFile( &m_FILE, ((HGOSFILE) file)->m_Filename, READONLY );
	unsigned char lpBuffer[1024];
	gos_SeekFile( m_FILE, FROMSTART, offset );
	gos_ReadFile( m_FILE, lpBuffer, 1024);
	WAVEFORMATEX * wf;
	GetWaveInfo(lpBuffer, &wf, &m_DataPointer, &m_DataLength);

	m_WaveFormatEx = (WAVEFORMATEX *) gos_Malloc(sizeof(WAVEFORMATEX));
	memcpy(m_WaveFormatEx, wf, sizeof(WAVEFORMATEX));
	m_hMusic = 0;
	m_playListDataLength = m_DataLength;


	duplicateCount = 0;
	memset( lpDuplicateBuffers, 0, sizeof(lpDuplicateBuffers) );
	memset( duplicateInUse, 0, sizeof(duplicateInUse) );

	//
	// If the wave data is too short, make it cached instead.
	//
	if (Environment.soundForceCache && m_DataLength < Environment.soundForceCache)
	{
		m_FILE = 0;
		gos_Free(m_WaveFormatEx);
		m_WaveFormatEx = 0;
		m_Type = gosAudio_CachedFile;
		LoadFile();
		m_fDuration = (float) m_DataLength / m_WaveFormatEx->nAvgBytesPerSec;
		return;
	}

	m_FileName = (char *) gos_Malloc(strlen(file_name)+1, SoundHeap);
	strcpy(m_FileName, file_name);

	if (m_WaveFormatEx->wFormatTag == WAVE_FORMAT_PCM)
	{
	    m_fDuration = (float) m_DataLength / m_WaveFormatEx->nAvgBytesPerSec;
		m_microsoftPCM = true;
	}
	else
	{
	    m_fDuration = (float) m_DataLength / m_WaveFormatEx->nAvgBytesPerSec;
		m_microsoftPCM = false;
								
		memset(&m_suggFormat, 0, sizeof(m_suggFormat));
		m_suggFormat.wFormatTag = WAVE_FORMAT_PCM;

   		wACMFormatSuggest(NULL, wf, &m_suggFormat, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG);
		wACMStreamOpen(&m_hACMStream,NULL,wf, &m_suggFormat, NULL,0,0,0);
		m_uncompressedData = (char *) gos_Malloc(16384, SoundHeap);
		// we need to make sure that the buffersize jives with what ACM wants
	}
	gos_SeekFile(m_FILE, FROMCURRENT, -1024 + (m_DataPointer-lpBuffer));
	m_totalBytesRead = 0;

	g_soundResources->Add(this);
}

SoundResource::SoundResource
(
	void * data,
	gosAudio_Format * wf,
	const char * name,
	int size,
	bool only2D
)
{
	gosASSERT(data);
	if (name)
	{
		m_FileName = (char *) gos_Malloc(strlen(name)+1, SoundHeap);
		strcpy(m_FileName, name);
	}
	else
		m_FileName = NULL;

	duplicateCount = 0;
	memset( lpDuplicateBuffers, 0, sizeof(lpDuplicateBuffers) );
	memset( duplicateInUse, 0, sizeof(duplicateInUse) );

	m_only2D = only2D;
	lpMasterBuffer = NULL;
	m_DataPointer = (unsigned char *) data;
	m_playList = NULL;
	m_FileDataPointer = NULL;
	m_FileLength = 0;
	m_DataLength = size;
	m_Flags = 0;
	m_Type = gosAudio_UserMemory;

	if (wf == NULL)
	{
		GetWaveInfo((unsigned char *) data, (WAVEFORMATEX **) &wf, (unsigned char **) &m_DataPointer, &m_DataLength);
	}
	m_WaveFormatEx = (WAVEFORMATEX *) gos_Malloc(sizeof(gosAudio_Format) + (wf->wFormatTag == WAVE_FORMAT_PCM ? 0: wf->cbSize), SoundHeap);
	memcpy(m_WaveFormatEx, wf, sizeof(gosAudio_Format) + (wf->wFormatTag == WAVE_FORMAT_PCM ? 0: wf->cbSize));

    m_nDataSize = 0;
    m_nBytesPlayed = 0;
	m_nBytesUncompressed = 0;
	if (m_WaveFormatEx->wFormatTag == WAVE_FORMAT_PCM)
	{
	    m_fDuration = (float) m_DataLength  / m_WaveFormatEx->nAvgBytesPerSec;
		m_microsoftPCM = true;
	}
	else
	{
	    m_fDuration = (float) m_DataLength / m_WaveFormatEx->nAvgBytesPerSec;
		m_microsoftPCM = false;
	}
	m_hACMStream = NULL;
	m_uncompressedData = NULL;
	m_uncompressedDataSize = 0;
	m_FileDataPointer = (unsigned char *)data;
	if (!Environment.soundDisable) 
		CreateMasterBuffer();
	g_soundResources->Add(this);
}


SoundResource::SoundResource
(
	const char * name,
	gosAudio_PlayList * playlist,
	bool only2D
)
{
	gosASSERT(playlist);
#ifdef _ARMOR
	if (!name)
		STOP(("Please supply a name by which this resource can be identified"));
#endif

	duplicateCount = 0;
	memset( lpDuplicateBuffers, 0, sizeof(lpDuplicateBuffers) );
	memset( duplicateInUse, 0, sizeof(duplicateInUse) );

	lpMasterBuffer = NULL;
	m_only2D = only2D;
	m_microsoftPCM = true;
	m_hACMStream = NULL;
	m_uncompressedDataSize = 0;
	m_uncompressedData = NULL;
	if (name)
	{
		m_FileName = (char *) gos_Malloc(strlen(name)+1, SoundHeap);
		strcpy(m_FileName, name);
	}
	else
		m_FileName = NULL;

	m_FileDataPointer = (unsigned char *) playlist->m_lpSoundData[0];



	//
	// make an internal copy of the playlist that was passed to us
	//
	m_playList = (gosAudio_PlayList *) gos_Malloc(sizeof(gosAudio_PlayList), SoundHeap);
	m_playList->m_dwListLength = playlist->m_dwListLength;
	m_playList->m_lpSoundData = (DWORD *) gos_Malloc(sizeof(DWORD *) * m_playList->m_dwListLength);
	memcpy(m_playList->m_lpSoundData, playlist->m_lpSoundData, sizeof(DWORD *) * m_playList->m_dwListLength);
	m_playList->m_lpDataLength = (DWORD *) gos_Malloc(sizeof(DWORD *) * m_playList->m_dwListLength);
	memcpy(m_playList->m_lpDataLength, playlist->m_lpDataLength, sizeof(DWORD *) * m_playList->m_dwListLength);

	//
	// we need to initialize this variable so the playlist starts with the first sample
	//
	m_placeInList = 0;
	m_playListOffsetWithin = 0;	
	//
	// now, compute the total size of the sound data
	//
	m_playListDataLength = 0;
	unsigned long  dl;
	unsigned char * dummy;
	WAVEFORMATEX * wfex;
	WAVEFORMATEX * wfex_s;
	GetWaveInfo((unsigned char *) m_playList->m_lpSoundData[0], &wfex_s, &dummy, &dl);

	for(int i = 0; i < m_playList->m_dwListLength; i++)
	{
		GetWaveInfo((unsigned char *) m_playList->m_lpSoundData[i], &wfex, (unsigned char **) &m_playList->m_lpSoundData[i], &dl);
#ifdef _ARMOR
		if (
			wfex_s->wFormatTag != wfex->wFormatTag ||			   
			wfex_s->nChannels != wfex->nChannels ||
		    wfex_s->nSamplesPerSec != wfex->nSamplesPerSec ||		   
			wfex_s->nAvgBytesPerSec != wfex->nAvgBytesPerSec ||
			wfex_s->nBlockAlign != wfex->nBlockAlign ||		   
		    wfex_s->wBitsPerSample != wfex->wBitsPerSample
			)		   
			STOP(("Item #%d in the UserMemoryPlayList Sound Resource must be the same format as the first item", i));
#endif
		m_playList->m_lpDataLength[i] = dl;
		if (wfex->wFormatTag == WAVE_FORMAT_ADPCM)
			dl *= 4;
		m_playListDataLength += dl;
	}

	m_fDuration = (float) m_playListDataLength / wfex_s->nAvgBytesPerSec;


	GetWaveInfo
	(
		m_FileDataPointer,
		&wfex,
		&m_DataPointer,
		&m_DataLength
	);

	m_WaveFormatEx = (WAVEFORMATEX *) gos_Malloc( sizeof(WAVEFORMATEX)+ (wfex->wFormatTag == WAVE_FORMAT_PCM ? 0 : wfex->cbSize), SoundHeap);

	memcpy(m_WaveFormatEx, wfex, sizeof(WAVEFORMATEX)+ (wfex->wFormatTag == WAVE_FORMAT_PCM ? 0 : wfex->cbSize));
	m_Type = gosAudio_UserMemoryPlayList;

	if ( m_WaveFormatEx->wFormatTag != WAVE_FORMAT_PCM )
	{
		if ( m_WaveFormatEx->wFormatTag != WAVE_FORMAT_ADPCM )
			STOP(( "GameOS only supports PCM and MSADPCM sound formats" )); 

		m_microsoftPCM = false;
   		MMRESULT m_mmr;
   		memset(&m_suggFormat,0,sizeof(m_suggFormat));
   		m_suggFormat.wFormatTag = 1;
		
		AllowFail = 1;
   		m_mmr = wACMFormatSuggest
   			(
   				NULL, 
   				m_WaveFormatEx, 
   				&m_suggFormat, 
   				sizeof(WAVEFORMATEX), 
   				ACM_FORMATSUGGESTF_WFORMATTAG
   			);
		AllowFail = 0;
		wACMFormatSuggest
   			(
   				NULL, 
   				m_WaveFormatEx, 
   				&m_suggFormat, 
	   			sizeof(WAVEFORMATEX), 
   				ACM_FORMATSUGGESTF_WFORMATTAG
   			);
		wACMStreamOpen(&m_hACMStream,NULL,&m_suggFormat, &m_suggFormat, NULL,0,0,0);
		m_uncompressedData = (char *) gos_Malloc(16384, SoundHeap);
	};

	g_soundResources->Add(this);
}


SoundResource::~SoundResource()
{
	if (m_FileName)
	{
		gos_Free(m_FileName);
		m_FileName = NULL;
	}

	switch(m_Type)
	{
	case gosAudio_CachedFile:
		if ( m_WaveFormatEx )
		{
			gos_Free( m_WaveFormatEx );
			m_WaveFormatEx = NULL;
		}
		if( lpMasterBuffer )
		{
			gos_ChangeHeapSize(SoundHeap, - (signed int) masterDesc.dwBufferBytes, 1 );
			for (int i = 0; i < this->duplicateCount; i++)
			{
				if (!this->duplicateInUse[i])
				{
					wRelease(this->lpDuplicateBuffers[i]);
					this->lpDuplicateBuffers[i] = 0;
				}
			}
			wRelease( lpMasterBuffer );
			lpMasterBuffer = 0;
		}
		break;
	case gosAudio_UserMemory:
		if (m_WaveFormatEx)
		{
			gos_Free(m_WaveFormatEx);
			m_WaveFormatEx = NULL;
		}
		if( lpMasterBuffer )
		{
			gos_ChangeHeapSize(SoundHeap, - (signed int) masterDesc.dwBufferBytes, 1 );
			for (int i = 0; i < this->duplicateCount; i++)
			{
				if (!this->duplicateInUse[i])
				{
					wRelease(this->lpDuplicateBuffers[i]);
					this->lpDuplicateBuffers[i] = 0;
				}
			}
			wRelease( lpMasterBuffer );
			lpMasterBuffer = 0;
		}
		break;
	case gosAudio_UserMemoryPlayList:
		if (m_hACMStream)
		{
			wACMStreamClose(m_hACMStream,0);
			m_hACMStream = NULL;
			if (m_uncompressedData)
			{
				gos_Free(m_uncompressedData);
				m_uncompressedData = NULL;
			}
		}

		if (m_WaveFormatEx)
		{
			gos_Free(m_WaveFormatEx);
			m_WaveFormatEx = NULL;
		}
		if (m_playList)
		{
			if (m_playList->m_lpSoundData)
			{
				gos_Free(m_playList->m_lpSoundData);
				m_playList->m_lpSoundData = 0;
			}
			if (m_playList->m_lpDataLength)
			{
				gos_Free(m_playList->m_lpDataLength);
				m_playList->m_lpDataLength = 0;
			}
			gos_Free(m_playList);
			m_playList = 0;
		}
		break;
	case gosAudio_StreamedFile:
	case gosAudio_StreamedFP:
		if (m_WaveFormatEx)
		{
			gos_Free(m_WaveFormatEx);
			m_WaveFormatEx = NULL;
		}
		if (m_FILE)
		{
			gos_CloseFile(m_FILE);
			m_FILE = 0;
		}
		if (m_uncompressedData)
		{
			gos_Free(m_uncompressedData);
			m_uncompressedData = 0;
		}
		break;
	case gosAudio_StreamedMusic:
		if (m_hMusic)
		{
			gosMusic_DestroyResource(&m_hMusic);
			m_hMusic = 0;
		}

		if (m_WaveFormatEx)
		{
			gos_Free(m_WaveFormatEx);
			m_WaveFormatEx = NULL;
		}
		break;

	}

	g_soundResources->Del(this);
}

void SoundResource::LoadFile()
{
//
// Read whole sound file into memory
//
    gos_PushCurrentHeap( SoundHeap );
	gos_GetFile( m_FileName, &m_FileDataPointer, &m_FileLength );
	gos_PopCurrentHeap();
	
	WAVEFORMATEX * tmp;
	GetWaveInfo( m_FileDataPointer,	&tmp, &m_DataPointer, &m_DataLength );
	int size = sizeof(WAVEFORMATEX) + ((tmp->wFormatTag == 1) ? 0 : tmp->cbSize);
	m_WaveFormatEx = (WAVEFORMATEX *) gos_Malloc(size, SoundHeap);
	memcpy(m_WaveFormatEx, tmp, size); 

	if (!Environment.soundDisable) 
		CreateMasterBuffer();

	gos_Free(m_FileDataPointer);
}

//*********************************************************
//DSGetWaveInfo -
// A Microsoft routine to parse a RIFF/WAVE section of memory.
//
//
//Input:
//
//	lpBuffer			Points to the RIFF/WAVE data.
//
//	lplpWaveFormatEX    Will receive address of WAVEFORMATEX header.
//
//  lplpWaveData		Will receive address of wave data.
//
//	lpWaveSize			Will receive length of wave data.
//
//Return:
//
//TRUE - Success
//*********************************************************
void SoundResource::GetWaveInfo
	( 
		unsigned char * lpBuffer,
		WAVEFORMATEX **lplpWaveFormatEX,
		unsigned char **lplpWaveData,
		DWORD *lpWaveSize 
	)
{
    DWORD *pdw;
    DWORD *pdwEnd;
    DWORD dwRiff;
    DWORD dwType;
    DWORD dwLength;

    pdw = (DWORD *)lpBuffer;
    dwRiff = *pdw++;
    dwLength = *pdw++;
    dwType = *pdw++;
	
	// if "RIFF" or "WAVE" not found
    if (dwRiff != 'FFIR' || dwType != 'EVAW')
	{
		STOP(( "Unable to finder header info block in sound memory image of \"%s\" (0x%x)", m_FileName, lpBuffer )); 
	}
    pdwEnd = (DWORD *)((BYTE *)pdw + dwLength);

	int found = 0;
    while (pdw < pdwEnd)
    {
        dwType = *pdw++;

        switch (dwType)
        {
			// "fmt "
        	case 0x20746d66:
				if (dwLength < sizeof(WAVEFORMAT))
				{
					STOP(( "Cannot find FMT section" ));
				}
		        dwLength = *pdw++;
            	*lplpWaveFormatEX = (WAVEFORMATEX *)pdw;
			    pdw = (unsigned long *) (((char *)pdw)+dwLength);
				found += 1;
            	break;

			// "data"
        	case 0x61746164:
		        dwLength = *pdw++;
                *lplpWaveData = (LPBYTE)pdw;
                *lpWaveSize = dwLength;
				found += 1;
            	break;
			//Skip over other "Chunks"
			default:
		        dwLength = *pdw++;
				pdw = (unsigned long *)(((char*)pdw)+dwLength);
        }

		if (found == 2)
			break;
    }

	if (found != 2)
		STOP(( "Could not decipher sound resource '%s'", m_FileName )); 
}

//
//  This will read wave data from the wave file.  Makre sure we're descended into
//	the data chunk, else this will fail bigtime!
// 	cbRead          - # of bytes to read.   
//	pbDest          - Destination buffer to put bytes.
//	cbActualRead- # of bytes actually read.
//

int SoundResource::ReadPCM( BYTE* pbDest, UINT bytestofill, bool loopMe, bool prevFailed )
{
	// we're pulling from a memory-resident playlist!
	if (m_Type == gosAudio_StreamedFile || m_Type == gosAudio_StreamedFP)
	{
		int srcDataSize = 0;
		if (m_totalBytesRead < m_DataLength)
		{
			ReadFile( m_FILE->m_hFile, pbDest, bytestofill, (DWORD*)&srcDataSize, NULL );
		}

		if (srcDataSize != bytestofill)
		{
			if (loopMe)
			{
				if (prevFailed)
					return -1;
				m_totalBytesRead += srcDataSize;
				Cue();
				int valread = ReadPCM(pbDest + srcDataSize, bytestofill - srcDataSize, loopMe, true );
				if (valread == -1)
					return -1;
				else 
					return srcDataSize + valread;
			}
			else
			{
				if (srcDataSize)
				{
					memset(pbDest+srcDataSize, m_WaveFormatEx->wBitsPerSample == 8 ? 0x80 : 0x00, bytestofill-srcDataSize);
				}
				else
				{
					memset(pbDest, m_WaveFormatEx->wBitsPerSample == 8 ? 0x80 : 0x00, bytestofill);
				}

			}
		}
		m_totalBytesRead += srcDataSize;
	}
	else
	{
		if (m_playListOffsetWithin >= m_playList->m_lpDataLength[m_placeInList])
		{
			// last sound done being converted. finish up & move on to next....
		
			int diff = m_playListOffsetWithin - m_playList->m_lpDataLength[m_placeInList];
			memcpy(pbDest,(const char *) m_playList->m_lpSoundData[m_placeInList]+m_playListOffsetWithin-diff,diff);
			m_placeInList++;
			if (m_placeInList >= m_playList->m_dwListLength)
				m_placeInList = 0;
			m_playListOffsetWithin = 0;
			return ReadPCM(pbDest+diff,bytestofill-diff, loopMe );
		}
		else
		{
			// still converting the current sound in playlist
			int diff = (m_playList->m_lpDataLength[m_placeInList] - m_playListOffsetWithin);
		
			if (bytestofill > diff)
			{
				memcpy(pbDest,(const char *) m_playList->m_lpSoundData[m_placeInList]+m_playListOffsetWithin, diff);
				m_placeInList++;
				if (m_placeInList >= m_playList->m_dwListLength)
					m_placeInList = 0;
				m_playListOffsetWithin = 0;
				ReadPCM(pbDest+diff,bytestofill - diff,loopMe );
				return bytestofill;
			}
			else
			{
				memcpy(pbDest,(const char *) m_playList->m_lpSoundData[m_placeInList]+m_playListOffsetWithin,bytestofill);
				m_playListOffsetWithin += bytestofill;
			}										    
		}
		   
	}
    return (bytestofill);
}

int SoundResource::ReadACM (BYTE * pbDest, UINT bytesToFill, bool loopMe, bool prevFailed)
{
	UINT originalNeed = bytesToFill;
	if (m_Type == gosAudio_UserMemoryPlayList)
	{
		if (m_uncompressedDataSize >= bytesToFill)
		{
			memcpy(pbDest,m_uncompressedData,bytesToFill);
			m_uncompressedDataSize -= bytesToFill;
			if (m_uncompressedDataSize)
			{
				memcpy(m_uncompressedData,m_uncompressedData+bytesToFill,m_uncompressedDataSize);
				return bytesToFill;
			}
		}

		// if there's some uncompressed data left, copy to the play buffer, update the dst pointer,
		// subtract the amount from the needed, and continue
		if (m_uncompressedDataSize)
		{
			memcpy(pbDest,m_uncompressedData,m_uncompressedDataSize);
			pbDest += m_uncompressedDataSize;
			bytesToFill -= m_uncompressedDataSize;
			m_uncompressedDataSize = 0;
		}

		ACMSTREAMHEADER ACMHeader;
		memset(&ACMHeader,0,sizeof(ACMHeader));
		ACMHeader.cbStruct = sizeof(ACMHeader);

		int 
			bufferSize = 8192;
		char * 
			srcBuffer = SoundRenderer.m_workBuffer;

		ACMHeader.cbSrcLength = bufferSize;
		ACMHeader.cbDstLength = bufferSize*4;
		ACMHeader.pbSrc = (unsigned char *) srcBuffer;
		ACMHeader.pbDst = (unsigned char *) m_uncompressedData;

		// we're pulling from a memory-resident playlist!
		if (m_playListOffsetWithin >= m_playList->m_lpDataLength[m_placeInList])
		{
			// last sound done being converted. finish up & move on to next....
			
			int diff = m_playListOffsetWithin - m_playList->m_lpDataLength[m_placeInList];
			memcpy(srcBuffer,(const char *) m_playList->m_lpSoundData[m_placeInList]+m_playListOffsetWithin-diff,diff);
			m_placeInList++;
			if (m_placeInList >= m_playList->m_dwListLength)
				m_placeInList = 0;
			m_playListOffsetWithin = 0;
			memcpy(srcBuffer+diff, (const char *) m_playList->m_lpSoundData[m_placeInList], bufferSize - diff);
		}
		else
		{
			// still converting the current sound in playlist
			int diff = (m_playList->m_lpDataLength[m_placeInList] - m_playListOffsetWithin);
		
			if (bufferSize > diff * 4)
			{
				memcpy(srcBuffer,(const char *) m_playList->m_lpSoundData[m_placeInList]+m_playListOffsetWithin, diff);
				m_playListOffsetWithin += diff;
				m_placeInList++;
				if (m_placeInList >= m_playList->m_dwListLength)
					m_placeInList = 0;
				m_playListOffsetWithin = 0;
			}
			else
			{
				memcpy(srcBuffer,(const char *) m_playList->m_lpSoundData[m_placeInList]+m_playListOffsetWithin,bufferSize);
				m_playListOffsetWithin += bufferSize;
			}										    
		}
	}
	else
	if (m_Type == gosAudio_StreamedFile || m_Type == gosAudio_StreamedFP)
	{
		if (m_uncompressedDataSize)
		{
			// there is uncompressed data in the decompression buffer
			if (m_uncompressedDataSize > bytesToFill)
			{
				// we have enough decompressed audio to suit our current needs.
				// copy it to the directsound buffer
				memcpy(pbDest, m_uncompressedData, bytesToFill);

				// shift the remainder down to 0th position in buffer
				memcpy(m_uncompressedData, m_uncompressedData+bytesToFill, m_uncompressedDataSize - bytesToFill);

				// recompute the remaining # of uncompressed bytes
				m_uncompressedDataSize -= bytesToFill;

				// we're done!
				return bytesToFill;
			}
			else
			{
				// we have SOME decompressed audio, but not enough.
				// copy what we do have into directsound buffer
				memcpy(pbDest, m_uncompressedData, m_uncompressedDataSize);

				// remove the amount we had from the bytes needed
				bytesToFill -= m_uncompressedDataSize;

				// increment were we are in the directsound buffer
				pbDest += m_uncompressedDataSize;

				// now, reset the amount of uncompressed data to 0
				m_uncompressedDataSize = 0;

				// we need to get more data still, go get it!
				ReadACM(pbDest, bytesToFill, loopMe );

				// when done, return that we got all we needed
				return originalNeed;
			}
		}
		else
		{
			// we have no uncompressed data in the decompression buffer, let's create some
			// NOTE: m_workBuffer is 8192 bytes long

			// read some ADPCM data from the file... 
			// !!! (be sure to check against file's data length,   !!!
			// !!! some ADPCM append data to the end               !!!
			int bytesRead = 0;
			
			if (m_totalBytesRead < m_DataLength)
			{
				ReadFile( m_FILE->m_hFile, SoundRenderer.m_workBuffer, 4096, (DWORD*)&bytesRead, NULL );
			}
			if (m_totalBytesRead + bytesRead > m_DataLength)
			{
				bytesRead = m_DataLength - m_totalBytesRead;
				m_totalBytesRead = m_DataLength;
			}
			else
				m_totalBytesRead += bytesRead;

			if (bytesRead != 4096)
				memset(SoundRenderer.m_workBuffer+bytesRead, m_suggFormat.wBitsPerSample == 8 ? 0x80 : 0x00, 4096-bytesRead);

			// compute what it'll be converted to PCM
			int bytesConverted = 16384;

			// just grabbed 8192 bytes worth of ADPCM data
			
			// set up the ACM structure
			if (bytesRead)
			{
				ACMSTREAMHEADER ACMHeader;
				memset(&ACMHeader,0,sizeof(ACMHeader));
				ACMHeader.cbStruct = sizeof(ACMHeader);
			 	ACMHeader.cbSrcLength = 4096;
				ACMHeader.cbDstLength = bytesConverted;
				ACMHeader.pbSrc = (unsigned char *) SoundRenderer.m_workBuffer;
				ACMHeader.pbDst = (unsigned char *) m_uncompressedData;

				// convert the ADPCM data (in workBuffer) to PCM data (into m_uncompressedData)
				wACMStreamPrepareHeader(m_hACMStream,&ACMHeader, 0);
				wACMStreamConvert(m_hACMStream,&ACMHeader, 0);
				wACMStreamUnprepareHeader(m_hACMStream,&ACMHeader, 0);
				// now, m_uncompressedData has PCM data in it

				bytesConverted = ACMHeader.cbDstLengthUsed;
			}
			else
				bytesConverted = 0;

			if (bytesConverted > bytesToFill)
			{
				// we uncompressed more data than we needed, copy first part
				memcpy(pbDest, m_uncompressedData, bytesToFill);
				memcpy(m_uncompressedData, m_uncompressedData+bytesToFill, bytesConverted-bytesToFill);
				m_uncompressedDataSize = bytesConverted-bytesToFill;
				return originalNeed;
			}
			else
			{
				// we still don't have enough data! copy what we do have, then get more!
				if (bytesRead == 0)
				{
					if (loopMe)
					{
						// get data from start of sound...
						if (prevFailed)
							return -1;
						Cue();
						int retcode = ReadACM(pbDest, bytesToFill, loopMe, true);
						if (retcode != -1)
							return originalNeed;
						else
							return -1;
					}
					else
					{
						// fill with silence!
						memset(pbDest,m_suggFormat.wBitsPerSample == 8 ? 0x80 : 0x00, bytesToFill);
					}
					return originalNeed;
				}
				else
				{
					memcpy(pbDest, m_uncompressedData, bytesConverted);
					m_uncompressedDataSize = 0;
					ReadACM(pbDest+bytesConverted, bytesToFill-bytesConverted, loopMe );
				}
				return originalNeed;
			}

		}
	}
	return bytesToFill;
}

void SoundResource::CloseStream()
{

	if (m_WaveFormatEx!= NULL)
	{
		gos_Free(m_WaveFormatEx);
		m_WaveFormatEx = NULL;
	}
}

//
// Cue
//
void SoundResource::Cue (void)
{
    // Seek to 'data' chunk from beginning of file
	if (m_Type == gosAudio_UserMemoryPlayList)
	{
		// Reset byte counter
		m_uncompressedDataSize = 0;
	    m_nBytesPlayed = 0;
		m_nBytesUncompressed = 0;
		m_placeInList = 0;
		m_playListOffsetWithin = 0;
	}
	else
	{
		if (m_Type == gosAudio_StreamedFile)
		{
			SetFilePointer( m_FILE->m_hFile, 90, 0, FILE_BEGIN );
		}
		else
		if (m_Type == gosAudio_StreamedFP)
		{
			SetFilePointer( m_FILE->m_hFile, -((int) m_totalBytesRead), 0, FILE_CURRENT );
		}

		m_nBytesUncompressed = 0;
		m_uncompressedDataSize = 0;
		m_totalBytesRead = 0;

	}

}

void SoundResource::OpenFromMemory()
{
	GetWaveInfo
	(
		m_FileDataPointer,
		&m_WaveFormatEx,
		&m_DataPointer,
		&m_DataLength
	);
}

void SetupStreamedWAV(SoundResource * s)
{
	s->m_FILE = 0;
	gos_PushCurrentHeap(SoundHeap);
	gos_OpenFile(&s->m_FILE, s->m_FileName, READONLY);
	gos_PopCurrentHeap();
	if (!s->m_FILE)
		STOP(("Could not find file \"%s\"", s->m_FileName));
	unsigned char lpBuffer[1024];
	gos_ReadFile( s->m_FILE, lpBuffer, 1024);
	WAVEFORMATEX * wf;
	s->GetWaveInfo(lpBuffer, &wf, &s->m_DataPointer, &s->m_DataLength);

	s->m_WaveFormatEx = (WAVEFORMATEX *) gos_Malloc(sizeof(WAVEFORMATEX));
	memcpy(s->m_WaveFormatEx, wf, sizeof(WAVEFORMATEX));
	s->m_hMusic = 0;
	s->m_playListDataLength = s->m_DataLength;


	//
	// If the wave data is too short, make it cached instead.
	//
	if (Environment.soundForceCache && s->m_DataLength < Environment.soundForceCache)
	{
		gos_CloseFile(s->m_FILE);
		s->m_FILE = 0;
		gos_Free(s->m_WaveFormatEx);
		s->m_WaveFormatEx = 0;
		s->m_Type = gosAudio_CachedFile;
		s->LoadFile();
		s->m_fDuration = (float) s->m_DataLength  / s->m_WaveFormatEx->nAvgBytesPerSec;
		return;
	}

	if (s->m_WaveFormatEx->wFormatTag == WAVE_FORMAT_PCM)
	{
	    s->m_fDuration = (float) s->m_DataLength / s->m_WaveFormatEx->nAvgBytesPerSec;
		s->m_microsoftPCM = true;
	}
	else
	{
	    s->m_fDuration = (float) (s->m_DataLength) / (s->m_WaveFormatEx->nAvgBytesPerSec);
		s->m_microsoftPCM = false;
								
		memset(&s->m_suggFormat, 0, sizeof(s->m_suggFormat));
		s->m_suggFormat.wFormatTag = WAVE_FORMAT_PCM;

   		wACMFormatSuggest(NULL, wf, &s->m_suggFormat, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG);
		wACMStreamOpen(&s->m_hACMStream,NULL,wf, &s->m_suggFormat, NULL,0,0,0);
		s->m_uncompressedData = (char *) gos_Malloc(16384, SoundHeap);
		// we need to make sure that the buffersize jives with what ACM wants
	}
	gos_SeekFile(s->m_FILE, FROMSTART, s->m_DataPointer - lpBuffer );
}

void SoundResource::GetDuplicateBuffer(LPDIRECTSOUNDBUFFER * lpDst)
{
	*lpDst = 0;

	//
	// The the sound isn't loaded we can't use the duplicate buffer system...
	//
	if (!( m_Type == gosAudio_CachedFile || m_Type == gosAudio_UserMemory) )
		return;

	if (m_only2D)
	{
		wAddRef(lpMasterBuffer);
		*lpDst = lpMasterBuffer;
		return;
	}

	for(int i = 0; i < duplicateCount; i++)
	{
		if (lpDuplicateBuffers[i] && !duplicateInUse[i])
		{
			*lpDst = lpDuplicateBuffers[i];
			duplicateInUse[i] = true;
			SPEW(( GROUP_DIRECTSOUND, "Returning previously created duplicate buffer (0x%x)", *lpDst));
			return;
		}
	}

	if (duplicateCount == 15)
	{
		SPEW(( GROUP_DIRECTSOUND, "Maximum number of duplicate buffers created for 0x%x", lpMasterBuffer ));
		*lpDst = NULL;
		return;
	}

	
	AllowFail = 1;
	HRESULT hr = wDuplicateSoundBuffer( SoundRenderer.m_Mixer->m_lpDirectSound, lpMasterBuffer, lpDst );
	AllowFail = 0;

	lpDuplicateBuffers[duplicateCount] = *lpDst;

	if (!hr)
	{
		duplicateInUse[duplicateCount] = true;
		duplicateCount++;
		SPEW(( GROUP_DIRECTSOUND, "Returning newly created duplicate buffer (0x%x)", *lpDst));
		return;
	}
	else
	{
		lpDuplicateBuffers[duplicateCount] = 0;
		SPEW(( GROUP_DIRECTSOUND, "Attempt to create a duplicate sound buffer failed (0x%x)", lpMasterBuffer ));
		*lpDst = NULL;
	}
}

void SoundResource::RelinquishDuplicate(LPDIRECTSOUNDBUFFER lpBuf)
{
	if (lpBuf == lpMasterBuffer)
		return;
	for(int i = 0; i < duplicateCount; i++)
	{
		if (lpBuf == lpDuplicateBuffers[i])
		{
			duplicateInUse[i] = false;
			SPEW(( GROUP_DIRECTSOUND, "Relinquished duplicate buffer to be used again (0x%x)", lpBuf));
			return;
		}
	}

#ifdef _DEBUG
	STOP(("Attempted to relinquish a bad sound buffer"));
#endif

}

void SoundResource::CreateMasterBuffer()
{
	LOG_BLOCK("gosAudio::Creating a Master Resource");
	HRESULT 		dsreturn;

	memset ( &masterDesc, 0, sizeof( DSBUFFERDESC ) );
    masterDesc.dwSize = sizeof(masterDesc);
	masterDesc.dwBufferBytes = m_DataLength;
	masterDesc.lpwfxFormat = m_WaveFormatEx;
	masterDesc.dwFlags = 0;

	// using the channel properties, create the initialization options for the buffer.
	masterDesc.dwFlags |= DSBCAPS_CTRLFREQUENCY;
	masterDesc.dwFlags |= DSBCAPS_CTRLPAN;
	// the mixer needs control of this, so add it by default!
	masterDesc.dwFlags |= DSBCAPS_CTRLVOLUME;
	if (!m_only2D)
		masterDesc.dwFlags |= DSBCAPS_CTRL3D;
	
	if (SoundRenderer.m_Mixer->m_dscaps.dwFlags & DSBCAPS_GETCURRENTPOSITION2)
		masterDesc.dwFlags|= DSBCAPS_GETCURRENTPOSITION2 ;


	// prepare for 3d buffer if necessary
	void * dst1;
	DWORD size1;
	SoundRenderer.m_Mixer->GetCaps();
	if (!m_only2D)
	{
		if ( SoundRenderer.m_Mixer->m_dscaps.dwFreeHw3DAllBuffers && Environment.soundMixInHardware )
		{
			// for true positional sound
			masterDesc.dwFlags |= DSBCAPS_CTRL3D;
		}
		else
		{
			// for fake positional sound
			masterDesc.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_LOCSOFTWARE;
			masterDesc.guid3DAlgorithm = DS3DALG_HRTF_LIGHT;
		}
	}

	if (!Environment.soundMixInHardware)
		masterDesc.dwFlags |= DSBCAPS_LOCSOFTWARE;

	// if the waveformat != PCM
	WAVEFORMATEX sugg;
	if ( m_WaveFormatEx->wFormatTag != WAVE_FORMAT_PCM )
	{
		if ( m_WaveFormatEx->wFormatTag != WAVE_FORMAT_ADPCM )
			STOP(( "GameOS only supports PCM and MSADPCM sound formats" )); 

		memset(&sugg,0,sizeof(sugg));
		sugg.wFormatTag = 1;

		MMRESULT mmr;
		AllowFail = 1;
		mmr = wACMFormatSuggest
			(
				NULL, 
				m_WaveFormatEx, 
				&sugg, 
				sizeof(WAVEFORMATEX), 
				ACM_FORMATSUGGESTF_WFORMATTAG
			);
		AllowFail = 0;
		if (mmr)
		{
			wACMFormatSuggest
   				(
   					NULL, 
   					m_WaveFormatEx, 
   					&sugg, 
	   				sizeof(WAVEFORMATEX), 
   					ACM_FORMATSUGGESTF_WFORMATTAG
   				);
		}

		HACMSTREAM hACMStream = NULL;

		DWORD estSrcSize = 0;
		DWORD estDstSize = 0;

		wACMStreamOpen(&hACMStream,NULL,m_WaveFormatEx,&sugg,NULL,0,0,0);
		wACMStreamSize(hACMStream, m_DataLength, &estDstSize,ACM_STREAMSIZEF_SOURCE);
		wACMStreamSize(hACMStream, estDstSize, &estSrcSize,ACM_STREAMSIZEF_DESTINATION);
		masterDesc.lpwfxFormat = &sugg;
		masterDesc.dwBufferBytes = estDstSize;

		// create regular buffer
		SafeFPU();					// Make sure exceptions cannot be generated
		dsreturn = wCreateSoundBuffer(SoundRenderer.m_Mixer->m_lpDirectSound, &masterDesc, &lpMasterBuffer, NULL);
		GameOSFPU();				// Restore current GameOS FPU control word setup

		ACMSTREAMHEADER ACMHeader;
		memset(&ACMHeader,0,sizeof(ACMHeader));

		void * dontCare1;
		DWORD dontCare2 = 0;
		void * dst1;
		wLock (lpMasterBuffer, 0, masterDesc.dwBufferBytes, &dst1, &size1, &dontCare1, &dontCare2, 0);

		ACMHeader.cbStruct = sizeof(ACMHeader);
		ACMHeader.cbDstLength = estDstSize;//m_currentSoundResource->m_DataLength*5;
		ACMHeader.cbSrcLength = m_DataLength;

		ACMHeader.pbSrc = (unsigned char *) m_DataPointer;
		ACMHeader.pbDst = (unsigned char *) dst1;

		//////////////////////////////////
		// we should check return values
		//////////////////////////////////
		wACMStreamPrepareHeader(hACMStream,&ACMHeader, 0);
		wACMStreamConvert(hACMStream,&ACMHeader, ACM_STREAMCONVERTF_BLOCKALIGN );
		wACMStreamUnprepareHeader(hACMStream,&ACMHeader, 0);


		memset((char *)dst1+ACMHeader.cbDstLengthUsed, m_WaveFormatEx->wBitsPerSample == 8 ? 0x80 : 0x00, estDstSize-ACMHeader.cbDstLengthUsed);

		wUnlock (lpMasterBuffer, ACMHeader.pbDst, size1, dontCare1, dontCare2);
		wACMStreamClose(hACMStream, 0);

	}
	else
	{
		// create regular buffer
		void * dontCare1;
		DWORD dontCare2 = 0;
		SafeFPU();					// Make sure exceptions cannot be generated
		wCreateSoundBuffer(SoundRenderer.m_Mixer->m_lpDirectSound, &masterDesc, &lpMasterBuffer, NULL);
		GameOSFPU();				// Restore current GameOS FPU control word setup
		wLock (lpMasterBuffer, 0, masterDesc.dwBufferBytes, &dst1, &size1, &dontCare1, &dontCare2, 0);
		memcpy (dst1,m_DataPointer,size1);
		wUnlock (lpMasterBuffer, dst1, size1, dontCare1, dontCare2);

	}
	gos_ChangeHeapSize(SoundHeap, masterDesc.dwBufferBytes, 1 );
}