#include "pch.hpp"
//===========================================================================//
// File:	 Sound DS3DMixer.cpp										     //
// Project:  GameOS					                                         //
// Contents: Direct Sound mixer												 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 03/23/99  JTR    Created													 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1998-1999, Microsoft, Inc.									 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
//
#include "Sound Renderer.hpp"
#include "Windows.hpp"
#include "MemoryManager.hpp"
#include "Music.hpp"

extern SoundRendererData SoundRenderer;
extern gos_Music * g_activeMusicList;
extern HWND hWindow;

DS3DSoundMixer::DS3DSoundMixer()
{
	SPEW((GROUP_DIRECTSOUND,  "Create DS3DSoundMixer()" ));

	// setup standard stuff
	m_fVolume = 1.0f;
	// setup default factors
	m_Attributes3D.dwSize = sizeof(m_Attributes3D);

	m_Attributes3D.flDopplerFactor = 1.0f; 
	m_Attributes3D.flRolloffFactor = 1.0f; 	
	m_Attributes3D.flDistanceFactor = 1.0f;	

	m_reverbFactor = .1f;	
	m_decayFactor = 0.0f; 

	// setup default positional information
	m_Attributes3D.vOrientFront.x = m_Attributes3D.vOrientFront.y = m_Attributes3D.vOrientTop.x   =  m_Attributes3D.vOrientTop.z   = 0.0f;
	m_Attributes3D.vOrientFront.z = m_Attributes3D.vOrientTop.y = 1.0f;
	m_Attributes3D.vPosition.x = m_Attributes3D.vPosition.y = m_Attributes3D.vPosition.z = 0.0f;
	m_Attributes3D.vVelocity.x = m_Attributes3D.vVelocity.y = m_Attributes3D.vVelocity.z = 0.0f;

	// setup the mixer's format
	memset ( &m_waveFormatEx, 0, sizeof(m_waveFormatEx) );
	m_waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
	m_waveFormatEx.nChannels = 2;
	m_waveFormatEx.wBitsPerSample = 16;
	m_waveFormatEx.nSamplesPerSec = Environment.soundHiFi ? 44100 : 22050;
    m_waveFormatEx.nBlockAlign     = (WORD)(m_waveFormatEx.wBitsPerSample / 8 * m_waveFormatEx.nChannels);
    m_waveFormatEx.nAvgBytesPerSec = m_waveFormatEx.nSamplesPerSec * m_waveFormatEx.nBlockAlign;

	m_changed = false;

	m_lpEffectsBuffer = NULL;
	m_lpListener = NULL;
	m_lpPrimaryBuffer = NULL;
	m_lpDirectSound = NULL;

	if (Environment.soundDevice >= SoundRenderer.m_numDevices)
		Environment.soundDevice = 0;

	SoundRenderer.m_PreferredDevice = Environment.soundDevice;

	gos_MathExceptions( 0, 0 );
	AllowFail=1;
	HRESULT hr = wDirectSoundCreate( &SoundRenderer.m_DeviceArray[SoundRenderer.m_PreferredDevice].Guid, &m_lpDirectSound, NULL );
	AllowFail=0;
	gos_MathExceptions( 1, 0 );
//
// Not being able to start direct sound currently just produces a warning.
//
	if( FAILED( hr ) )
	{
		SPEW(( GROUP_DIRECTSOUND, "DirectSound was unable to initialize" ));
		Environment.soundDisable = true;
		m_lpDirectSound=0;
		return;
	}
//
// First set cooperative level
//
	wSetCooperativeLevel( m_lpDirectSound, hWindow, DSSCL_PRIORITY);
//
// Get caps of DSound device
//
	GetCaps();
//
// Create the primary buffer
//
	m_properties = 
		gosAudio_Volume |
		gosAudio_Panning |
		gosAudio_Position |
		gosAudio_Velocity |
		gosAudio_FrontOrientation |
		gosAudio_TopOrientation |
		gosAudio_Doppler |
		gosAudio_Rolloff |
		gosAudio_Distance;

	DSBUFFERDESC dsbd;
	memset( &dsbd, 0, sizeof(dsbd) );
    dsbd.dwSize = sizeof(dsbd);
    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;

	AllowFail = 1;
	SafeFPU();					// Make sure exceptions cannot be generated
	hr = wCreateSoundBuffer( m_lpDirectSound, &dsbd, &m_lpPrimaryBuffer, NULL);
	GameOSFPU();				// Restore current GameOS FPU control word setup
	AllowFail = 0;
	if ( FAILED( hr) )
	{
		SPEW(( GROUP_DIRECTSOUND, "DirectSound was unable to create a mixer" ));
		wRelease(m_lpDirectSound);
		m_lpDirectSound=0;
		Environment.soundDisable = true;
		return;
	}

	wSetFormat( m_lpPrimaryBuffer, &m_waveFormatEx);
//
// Get primary buffer details and record size in memory manager
//
	m_mixerCaps.dwSize = sizeof(m_mixerCaps);
	wGetCaps(m_lpPrimaryBuffer, &m_mixerCaps);
	gos_ChangeHeapSize(SoundHeap, m_mixerCaps.dwBufferBytes, 1 );
//
// Create a 3D listner
//
	wQueryInterface( m_lpPrimaryBuffer, IID_IDirectSound3DListener, (void **)&m_lpListener);
	wSetOrientation( m_lpListener, m_Attributes3D.vOrientFront.x, m_Attributes3D.vOrientFront.y, m_Attributes3D.vOrientFront.z, 
		m_Attributes3D.vOrientTop.x, m_Attributes3D.vOrientTop.y, m_Attributes3D.vOrientTop.z, DS3D_DEFERRED );
//
// Now setup all the channels the application has asked for
//
	SoundRenderer.m_numChannels = Environment.soundChannels;
	SPEW(( GROUP_DIRECTSOUND, "Creating %d Sound Channels...", SoundRenderer.m_numChannels));
	for(int i = 0; i < SoundRenderer.m_numChannels; i++)
	{
		SoundRenderer.m_Channel[i] = new(SoundHeap) DS3DSoundChannel();
		SoundRenderer.m_Channel[i]->m_myEnum = i;
	}
//
// Create an 3d sound buffer
//
	WAVEFORMATEX tempFORMAT;
	memset( &tempFORMAT, 0, sizeof(tempFORMAT) );
    tempFORMAT.wFormatTag      = WAVE_FORMAT_PCM; 
    tempFORMAT.nChannels       = 1; 
    tempFORMAT.nSamplesPerSec  = 44100; 
    tempFORMAT.wBitsPerSample  = 16; 
    tempFORMAT.nBlockAlign     = (WORD)(tempFORMAT.wBitsPerSample / 8 * tempFORMAT.nChannels);
    tempFORMAT.nAvgBytesPerSec = tempFORMAT.nSamplesPerSec * tempFORMAT.nBlockAlign;

	LPDIRECTSOUNDBUFFER tmp = NULL;
	memset ( &dsbd, 0, sizeof( dsbd ) );
	dsbd.dwFlags = DSBCAPS_CTRL3D;
	dsbd.dwSize = sizeof( DSBUFFERDESC );
	dsbd.lpwfxFormat = &tempFORMAT;
	dsbd.dwBufferBytes = 88200;
	dsbd.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;

	SafeFPU();					// Make sure exceptions cannot be generated
	wCreateSoundBuffer(m_lpDirectSound, &dsbd, &tmp, NULL);
	GameOSFPU();				// Restore current GameOS FPU control word setup

	wQueryInterface(tmp, IID_IDirectSound3DBuffer, (void **) &m_lpEffectsBuffer);
	wRelease(tmp);
//
// Create an effects buffer
//
	gos_MathExceptions( 0, 0 );
	LPKSPROPERTYSET lpPropSet;
	ULONG support = 0;
	AllowFail = 1;
	hr = wQueryInterface(m_lpEffectsBuffer, IID_IKsPropertySet, (VOID **) &lpPropSet);
	AllowFail = 0;

	if( FAILED(hr) )
	{
		lpPropSet = 0;
		wRelease(m_lpEffectsBuffer);
		m_lpEffectsBuffer = 0;
	}
	else
	{
		AllowFail = 1;
		hr = wQuerySupport(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DECAYTIME, &support);
		if( hr==S_OK && (support & KSPROPERTY_SUPPORT_SET) )
			wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DECAYTIME,NULL, 0, &m_reverbFactor, sizeof(float));

		hr = wQuerySupport(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DAMPING, &support);
		if( hr==S_OK && (support & KSPROPERTY_SUPPORT_SET) )
			wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DAMPING,NULL, 0, &m_decayFactor, sizeof(float));

		DWORD env = EAX_ENVIRONMENT_GENERIC;
		hr = wQuerySupport(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ENVIRONMENT, &support);
		if( hr==S_OK && (support & KSPROPERTY_SUPPORT_SET) )
			wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ENVIRONMENT, NULL, 0, &env, sizeof(DWORD));
		AllowFail = 0;

		wRelease(lpPropSet);
	}
	gos_MathExceptions( 1, 0 );

	SoundRenderer.m_isInitialized = true;
}

DS3DSoundMixer::~DS3DSoundMixer()
{
	SPEW((GROUP_DIRECTSOUND,  "Destroy DS3DSoundMixer()" ));
	if (m_lpEffectsBuffer)
	{
		wRelease(m_lpEffectsBuffer);
		m_lpEffectsBuffer=0;
	}
	if (m_lpListener)
	{
		wRelease(m_lpListener);
		m_lpListener=0;
	}
	if (m_lpPrimaryBuffer)
	{
		gos_ChangeHeapSize(SoundHeap, -((signed int)m_mixerCaps.dwBufferBytes), 1 );
		wRelease(m_lpPrimaryBuffer);
		m_lpPrimaryBuffer=0;
	}
	if (m_lpDirectSound)
	{
		wRelease(m_lpDirectSound);
		m_lpDirectSound=0;
	}
}

void DS3DSoundMixer::Flush()
{
	// fake 3d sounds (which just use panning and volume) are handled here
	bool needFlush = false;
	DS3DSoundChannel * s = NULL;
	for(int i = 0; i < SoundRenderer.m_numChannels; i++)
	{
		s = SoundRenderer.m_Channel[i];
		if ( s->m_currentProperties & gosAudio_Position && s->m_changed )
		{
			needFlush = true;
			s->m_changed = false;
		}
	}
	
	// send any changes to the true 3d sounds down to the mixer

	if (needFlush || SoundRenderer.m_Mixer->m_changed)
	{
		LOG_BLOCK("GosAudio::Committing Deferred Commands");
		AllowFail = 1;
		SafeFPU();					// Make sure exceptions cannot be generated
		wCommitDeferredSettings(m_lpListener);
		GameOSFPU();				// Restore current GameOS FPU control word setup
		AllowFail = 0;
		SoundRenderer.m_Mixer->m_changed = false;
	}

	for(i = 0; i < SoundRenderer.m_numChannels; i++)
	{
		s = SoundRenderer.m_Channel[i];
	   	if (s->playPending)
		{
			LOG_BLOCK("GosAudio::Starting Qeued Sound");
			s->playPending = false;
			if (s->m_resourceType == gosAudio_UserMemory ||
				s->m_resourceType == gosAudio_CachedFile)
				s->Play();
		}
	}
}

void DS3DSoundMixer::SetVolume(float v)
{
	gosASSERT(v >= 0.0f && v <= 1.0f);
	m_fVolume = v;

	for(int i = 0; i < SoundRenderer.m_numChannels; i++)
	{
		SoundRenderer.m_Channel[i]->SetVolume(SoundRenderer.m_Channel[i]->m_fVolume);
	}

	HGOSMUSIC music = g_activeMusicList;
	while(music)
	{
		gosMusic_Command(music, gosMusic_Volume, music->m_fVolume);
		music = music->m_pNext;
	}
}

void DS3DSoundMixer::SetPosition(float x, float y, float z)
{
	m_Attributes3D.vPosition.x = x;
	m_Attributes3D.vPosition.y = y;
	m_Attributes3D.vPosition.z = z;
	wSetPosition(m_lpListener, x,y, z, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundMixer::SetVelocity(float x, float y, float z)
{
	m_Attributes3D.vVelocity.x = x;
	m_Attributes3D.vVelocity.y = y;
	m_Attributes3D.vVelocity.z = z;
	wSetVelocity(m_lpListener, x,y, z, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundMixer::SetTopOrientation( float x, float y, float z )
{
	m_Attributes3D.vOrientTop.x = x;
	m_Attributes3D.vOrientTop.y = y;
	m_Attributes3D.vOrientTop.z = z;
	wSetOrientation(m_lpListener, m_Attributes3D.vOrientFront.x, m_Attributes3D.vOrientFront.y, m_Attributes3D.vOrientFront.z, x, y, z, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundMixer::SetFrontOrientation( float x, float y, float z )
{
	m_Attributes3D.vOrientFront.x = x;
	m_Attributes3D.vOrientFront.y = y;
	m_Attributes3D.vOrientFront.z = z;
	wSetOrientation(m_lpListener, x, y, z, m_Attributes3D.vOrientTop.x, m_Attributes3D.vOrientTop.y, m_Attributes3D.vOrientTop.z, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundMixer::SetDopplerFactor( float df )
{
	m_Attributes3D.flDopplerFactor = df;
	wSetDopplerFactor(m_lpListener, df, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundMixer::SetRolloffFactor( float rf )
{
	m_Attributes3D.flRolloffFactor = rf;
	wSetRolloffFactor(m_lpListener, rf, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundMixer::SetDistanceFactor( float df )
{
	m_Attributes3D.flDistanceFactor = df;
	wSetDistanceFactor(m_lpListener, df, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundMixer::SetReverbFactor( float rf )
{
	m_reverbFactor = rf;

	if (rf < 0.1f) 
		rf = 0.1f;
	else if (rf > 20.0f)
		rf = 20.0f;

	LPKSPROPERTYSET lpPropSet;
	ULONG support;

	AllowFail=1;
	HRESULT hr=wQueryInterface(m_lpEffectsBuffer, IID_IKsPropertySet, (VOID **)&lpPropSet);
	AllowFail=0;

	if( SUCCEEDED(hr) )
	{
		AllowFail=1;
		hr=wQuerySupport(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DECAYTIME, &support);
		AllowFail=0;

		if( S_OK==hr )
		{
			if( support & KSPROPERTY_SUPPORT_SET )
			{
				wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DECAYTIME,NULL, 0, &m_reverbFactor, sizeof(float) );
				m_changed = true;
			}
		}
		wRelease(lpPropSet);
	}
}

void DS3DSoundMixer::SetDecayFactor( float df )
{
	m_decayFactor = df;

	if (df < 0.0f) 
		df = 0.0f;
	else if (df > 2.0f)
		df = 2.0f;

	LPKSPROPERTYSET lpPropSet;
	ULONG support;

	AllowFail=1;
	HRESULT hr=wQueryInterface(m_lpEffectsBuffer, IID_IKsPropertySet, (VOID **)&lpPropSet);
	AllowFail=0;

	if( SUCCEEDED(hr) )
	{
		AllowFail=1;
		hr=wQuerySupport(lpPropSet,DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DAMPING, &support);
		AllowFail=0;

		if( S_OK==hr )
		{
			if( support & KSPROPERTY_SUPPORT_SET )
			{
				wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DAMPING,NULL, 0, &m_decayFactor, sizeof(float) );
				m_changed = true;
			}
		}
		wRelease(lpPropSet);
	}
}

void DS3DSoundMixer::GetCaps()
{
	memset(&m_dscaps,0, sizeof(DSCAPS));
	m_dscaps.dwSize = sizeof(DSCAPS);
	wGetCaps(m_lpDirectSound, &m_dscaps);
}

void DS3DSoundMixer::SetSpeakerConfig( DWORD gFlags)
{
	DWORD dsflags = 0;
	if ( gFlags == gosAudio_Headphones)
		dsflags = DSSPEAKER_HEADPHONE;
	else
	if ( gFlags == gosAudio_Monaural)
		dsflags = DSSPEAKER_MONO;
	else
	if ( gFlags == gosAudio_Quadraphonic)
		dsflags = DSSPEAKER_QUAD;

	else
	if ( gFlags == gosAudio_Surround)
		dsflags = DSSPEAKER_SURROUND;
	else
	if ( gFlags & gosAudio_Stereo)
	{
		dsflags = DSSPEAKER_STEREO;
		if (gFlags & 0xf0)
		{
			if ( (gFlags & gosAudio_DegreeArc5) )
				dsflags = DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_MIN );
			else
			if ( (gFlags & gosAudio_DegreeArc10) )
				dsflags = DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_NARROW );
			else
			if ( (gFlags & gosAudio_DegreeArc20) )
				dsflags = DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE );
			else
			if ( (gFlags & gosAudio_DegreeArc180) )
				dsflags = DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_MAX );
		}
	}

	wSetSpeakerConfig( m_lpDirectSound, dsflags );
}

void DS3DSoundMixer::GetSpeakerConfig( DWORD* gflags)
{
	DWORD dsflags;
	wGetSpeakerConfig( m_lpDirectSound, &dsflags );
	*gflags = 0;
	if ( dsflags == DSSPEAKER_HEADPHONE)
		*gflags = gosAudio_Headphones;
	else
	if ( dsflags == DSSPEAKER_MONO)
		*gflags = gosAudio_Monaural;
	else
	if ( dsflags == DSSPEAKER_QUAD)
		*gflags = gosAudio_Quadraphonic;
	else
	if ( dsflags == DSSPEAKER_SURROUND)
		*gflags = gosAudio_Surround;
	else
	if ( dsflags == DSSPEAKER_STEREO)
		*gflags = gosAudio_Stereo;
	else
	if ( dsflags == DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_MIN ) )
		*gflags = gosAudio_Stereo | gosAudio_DegreeArc5;
	else
	if ( dsflags == DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_NARROW ) )
		*gflags = gosAudio_Stereo | gosAudio_DegreeArc10;
	else
	if ( dsflags == DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE ) )
		*gflags = gosAudio_Stereo | gosAudio_DegreeArc20;
	else
	if ( dsflags == DSSPEAKER_COMBINED( DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_MAX ) )
		*gflags = gosAudio_Stereo | gosAudio_DegreeArc180;
}

void DS3DSoundMixer::Reset()
{
	SPEW((GROUP_DIRECTSOUND,  "Resetting DS3DSoundMixer()" ));
	
	if (m_lpEffectsBuffer)
	{
		wRelease(m_lpEffectsBuffer);
		m_lpEffectsBuffer = NULL;
	}
	if (m_lpListener)
	{
		wRelease(m_lpListener);
		m_lpListener = NULL;
	}
	if (m_lpPrimaryBuffer)
	{
		gos_ChangeHeapSize(SoundHeap, -((signed int)m_mixerCaps.dwBufferBytes), 1 );
		wRelease(m_lpPrimaryBuffer);
		m_lpPrimaryBuffer = NULL;
	}
	if (m_lpDirectSound)
	{
		wRelease(m_lpDirectSound);
		m_lpDirectSound = NULL;
	}

	gos_MathExceptions( 0, 0 );

	if (Environment.soundDevice >= SoundRenderer.m_numDevices)
		Environment.soundDevice = 0;

	SoundRenderer.m_PreferredDevice = Environment.soundDevice;

	AllowFail=1;
	HRESULT hr = wDirectSoundCreate( &SoundRenderer.m_DeviceArray[SoundRenderer.m_PreferredDevice].Guid, &m_lpDirectSound, NULL );
	AllowFail=0;

	gos_MathExceptions( 1, 0 );
//
// Not being able to start direct sound currently just produces a warning.
//
	if( FAILED( hr ) )
	{
		SPEW(( GROUP_DIRECTSOUND, "DirectSound was unable to reinitialize" ));

		if (m_lpDirectSound)
		{
			wRelease(m_lpDirectSound);
			m_lpDirectSound = NULL;
		}
		Environment.soundDisable = true;
		SoundRenderer.m_isInitialized = false;
		return;
	}

	GetCaps();
	
	DSBUFFERDESC dsbd;
	memset ( &dsbd, 0, sizeof(dsbd) );
    dsbd.dwSize = sizeof(dsbd);
    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRL3D ;
	dsbd.lpwfxFormat = NULL;

	m_properties = 
		gosAudio_Volume |
		gosAudio_Panning |
		gosAudio_Position |
		gosAudio_Velocity |
		gosAudio_FrontOrientation |
		gosAudio_TopOrientation |
		gosAudio_Doppler |
		gosAudio_Rolloff |
		gosAudio_Distance;

	wSetCooperativeLevel( m_lpDirectSound, hWindow, DSSCL_PRIORITY);
	SafeFPU();					// Make sure exceptions cannot be generated
	wCreateSoundBuffer( m_lpDirectSound, &dsbd, &m_lpPrimaryBuffer, NULL);
	GameOSFPU();				// Restore current GameOS FPU control word setup
	wSetFormat( m_lpPrimaryBuffer, &m_waveFormatEx);
 	
	// caps/heap tracking
	m_mixerCaps.dwSize = sizeof(DSBCAPS);
	wGetCaps(m_lpPrimaryBuffer, &m_mixerCaps);

	gos_ChangeHeapSize(SoundHeap, m_mixerCaps.dwBufferBytes, 1 );

	wQueryInterface( m_lpPrimaryBuffer, IID_IDirectSound3DListener, (void **)&m_lpListener);

	wSetOrientation( m_lpListener, m_Attributes3D.vOrientFront.x, m_Attributes3D.vOrientFront.y, m_Attributes3D.vOrientFront.z, 
		m_Attributes3D.vOrientTop.x, m_Attributes3D.vOrientTop.y, m_Attributes3D.vOrientTop.z, DS3D_DEFERRED );

	SoundRenderer.m_numChannels = Environment.soundChannels;
	gosASSERT( Environment.soundChannels <= MAX_SOUNDCHANNELS );
	for(int i = Environment.soundChannels; i < SoundRenderer.m_numChannels; i++)
	{
		if (SoundRenderer.m_Channel[i]) 
			delete SoundRenderer.m_Channel[i];
	}

	// create an effects buffer
	WAVEFORMATEX tempFORMAT;
	memset( &tempFORMAT, 0, sizeof(tempFORMAT) );
	tempFORMAT.wFormatTag = WAVE_FORMAT_PCM;
	tempFORMAT.nChannels = 1;
	tempFORMAT.nSamplesPerSec = 22050;
	tempFORMAT.wBitsPerSample = 16;
    tempFORMAT.nBlockAlign     = (WORD)(tempFORMAT.wBitsPerSample / 8 * tempFORMAT.nChannels);
    tempFORMAT.nAvgBytesPerSec = tempFORMAT.nSamplesPerSec * tempFORMAT.nBlockAlign;

	LPDIRECTSOUNDBUFFER tmp = NULL;
	memset ( &dsbd, 0, sizeof( DSBUFFERDESC ) );
	dsbd.dwFlags = DSBCAPS_CTRL3D;
	dsbd.dwSize = sizeof( DSBUFFERDESC );
	dsbd.lpwfxFormat = &tempFORMAT;
	dsbd.dwBufferBytes = 32768;
	dsbd.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;

	if (!Environment.soundDisable) 
	{
		SafeFPU();					// Make sure exceptions cannot be generated
		wCreateSoundBuffer(m_lpDirectSound, &dsbd, &tmp, NULL);
		GameOSFPU();				// Restore current GameOS FPU control word setup
	
	// create a 3d buffer if necessary
		wQueryInterface(tmp, IID_IDirectSound3DBuffer, (void **)&m_lpEffectsBuffer);
		wRelease(tmp);

		gos_MathExceptions( 0, 0 );
		LPKSPROPERTYSET lpPropSet;
		ULONG support = 0;
		AllowFail = 1;
		hr = wQueryInterface(m_lpEffectsBuffer, IID_IKsPropertySet, (VOID **)&lpPropSet);
		AllowFail = 0;
		if( FAILED(hr) )
		{
			lpPropSet = 0;
			wRelease(m_lpEffectsBuffer);
			m_lpEffectsBuffer = 0;
		}
		else
		{
			AllowFail = 1;
			hr = wQuerySupport(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DECAYTIME, &support);
			AllowFail = 0;
			if( S_OK==hr )
			{
				if( support & KSPROPERTY_SUPPORT_SET )
				{
					DWORD env = EAX_ENVIRONMENT_GENERIC;
					wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ENVIRONMENT, NULL, 0, &env, sizeof(DWORD));
					wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DAMPING,NULL, 0, &m_decayFactor, sizeof(float));
					wSet(lpPropSet, DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DECAYTIME,NULL, 0, &m_reverbFactor, sizeof(float));
					m_properties |= gosAudio_Reverb | gosAudio_Decay;
				}
			}
			wRelease(lpPropSet);
		}
		gos_MathExceptions( 1, 0 );

		SPEW(( GROUP_DIRECTSOUND, "Recreating %d Sound Channels...", SoundRenderer.m_numChannels));
		for(i = 0; i < SoundRenderer.m_numChannels; i++)
		{
			if (SoundRenderer.m_Channel[i])
				SoundRenderer.m_Channel[i]->Reset(i);
			else
				SoundRenderer.m_Channel[i] = new(SoundHeap) DS3DSoundChannel();
		}
	}
}


