#include "pch.hpp"		 
//===========================================================================//
// File:	 Sound Channel.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 "ErrorHandler.hpp"
#include "DirectX.hpp"
#include "MemoryManager.hpp"
#include "ACM.hpp"
#include "Debugger.hpp"

extern CRITICAL_SECTION g_soundCritSec;
extern SoundRendererData SoundRenderer;
extern DWORD gLanguageDLL;
extern bool WindowsNT;

DS3DSoundChannel::DS3DSoundChannel()
{
	SPEW((GROUP_DIRECTSOUND, "Creating DS3DSoundChannel: (0x%x)", this));
	// clear all important data to default values
	m_fVolume = 1.0f;									// full volume
	m_fFrequency = 1.0f;								// normal playback frequency
	m_fPanning = 0.0f;									// audio is centered between speakers
	m_currentProperties = 0;							// no "fancy" properties
	m_oldProperties = 0;								// no "fancy" properties
	m_bLoopFlag = FALSE;								// audio doesn't loop
	m_currentSoundResource = NULL;						// no sound loaded
    m_fPlaying = m_fPaused = m_fCued = FALSE;			// nothing playing, paused or cued
    m_cbBufOffset = 0;									// no offset into sound buffer
    m_nBufLength = 2000;								// standard buffer length for streaming sounds
    m_cbBufSize = 0;									// another size value for streaming
	m_resourceName[0] = 0;
	m_resourceType = (gosAudio_ResourceType) -1;
	/////////////////
	/// 3d stuff
	m_Attributes3D.dwSize = sizeof(DS3DBUFFER);
	m_Attributes3D.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
	m_Attributes3D.dwMode = DS3DMODE_NORMAL;
	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;
	m_Attributes3D.vConeOrientation.x = m_Attributes3D.vConeOrientation.y = 0.0f;
	m_Attributes3D.vConeOrientation.z = 1.0f;
	m_Attributes3D.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
	m_Attributes3D.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
	m_Attributes3D.flMinDistance = 10.0f;
	m_Attributes3D.flMaxDistance = 100.0f;
	m_true3D = m_sysPause = false;
	m_changed = true;
	playPending = false;
	bIsDuplicate = false;
	m_Buffer = NULL;						// no sound loaded
	m_Buffer3D = NULL;
}

DS3DSoundChannel::~DS3DSoundChannel()
{
	SPEW((GROUP_DIRECTSOUND, "Destroying DS3DSoundChannel: (0x%x)", this));
	ClearAndFree();
}

void DS3DSoundChannel::ClearAndFree()
{
	ReleaseCurrent();
	m_resourceType = (gosAudio_ResourceType) -1;
	m_currentSoundResource = NULL;
	m_resourceName[0] = 0;
	bIsDuplicate = false;
}

void DS3DSoundChannel::CreateAndLoadBuffer( SoundResource* sound_res )
{
	EnterCriticalSection(&g_soundCritSec);

	m_resourceType = sound_res->m_Type;
	if (sound_res->m_FileName)
		strncpy(m_resourceName, sound_res->m_FileName,127);

	if ( m_currentSoundResource == sound_res && m_oldProperties == m_currentProperties)
	{
		// restart playing //
			SetVolume( m_fVolume );
		if (m_currentProperties & gosAudio_Frequency)
			SetFrequency( m_fFrequency );
		if (m_currentProperties & gosAudio_Panning)
			SetPan( m_fPanning );
	
		if ( m_currentProperties & gosAudio_MinMaxDistance )
			SetDistanceMinMax( m_Attributes3D.flMinDistance, m_Attributes3D.flMaxDistance );
	
		if (m_currentProperties & gosAudio_Position)
			SetPosition( m_Attributes3D.vPosition.x, m_Attributes3D.vPosition.y, m_Attributes3D.vPosition.z );
	
		if (m_currentProperties & gosAudio_Velocity)
			SetVelocity( m_Attributes3D.vVelocity.x, m_Attributes3D.vVelocity.y, m_Attributes3D.vVelocity.z );
	
		if (m_currentProperties & gosAudio_ConeOrientation)
			SetConeOrientation( m_Attributes3D.vConeOrientation.x, m_Attributes3D.vConeOrientation.y, m_Attributes3D.vConeOrientation.z );
		
		if (m_currentProperties & gosAudio_ConeAngles)
			SetConeAngles( m_Attributes3D.dwInsideConeAngle, m_Attributes3D.dwOutsideConeAngle );
		
		LeaveCriticalSection(&g_soundCritSec);
		return;
	}

	m_oldProperties = m_currentProperties;

	ReleaseCurrent();

	m_currentSoundResource = sound_res;
    m_fDuration = m_currentSoundResource->m_fDuration;

	if(!SoundRenderer.m_Mixer)
		STOP(( "Sound Mixer isn't initialized" ));

	// Create a buffer
	if (m_resourceType == gosAudio_StreamedFile || m_resourceType == gosAudio_StreamedFP)
	{
		CreateStreamBuffer(sound_res);
	}
	else
	{
		CreateBuffer();	
	}

	SetVolume( m_fVolume );
	if (m_currentProperties & gosAudio_Frequency)
		SetFrequency( m_fFrequency );
	if (m_currentProperties & gosAudio_Panning)
		SetPan( m_fPanning );

	if (m_currentProperties & gosAudio_Position)
		SetPosition( m_Attributes3D.vPosition.x, m_Attributes3D.vPosition.y, m_Attributes3D.vPosition.z );

	if (m_currentProperties & gosAudio_Velocity)
		SetVelocity( m_Attributes3D.vVelocity.x, m_Attributes3D.vVelocity.y, m_Attributes3D.vVelocity.z );
	
	if (m_currentProperties & gosAudio_ConeOrientation)
		SetConeOrientation( m_Attributes3D.vConeOrientation.x, m_Attributes3D.vConeOrientation.y, m_Attributes3D.vConeOrientation.z );
	
	if (m_currentProperties & gosAudio_ConeAngles)
		SetConeAngles( m_Attributes3D.dwInsideConeAngle, m_Attributes3D.dwOutsideConeAngle );
	
	if ( m_currentProperties & gosAudio_MinMaxDistance )
		SetDistanceMinMax( m_Attributes3D.flMinDistance, m_Attributes3D.flMaxDistance );

	LeaveCriticalSection(&g_soundCritSec);
}

void DS3DSoundChannel::CreateBuffer()
{
	ReleaseCurrent();

	m_currentSoundResource->GetDuplicateBuffer(&m_Buffer);
	if (m_Buffer && (m_Buffer != m_currentSoundResource->lpMasterBuffer))
	{
		SpewCaps();
		if (m_dsbCaps.dwFlags & DSBCAPS_LOCHARDWARE)
			m_true3D = true;
		else
			m_true3D = false;

		bIsDuplicate = true;
	}
	else
		bIsDuplicate = false;

	if (m_currentSoundResource->m_only2D)
		bOnly2D = true;
	else
		bOnly2D = false;

	// create a 3d buffer if necessary (only if there's hardware 3d!)
	if (m_Buffer)
	{
		if ( m_currentProperties & gosAudio_Position || m_currentProperties & gosAudio_Velocity || m_currentProperties & gosAudio_MinMaxDistance)
		{
//			if (dsbd.dwFlags & DSBCAPS_CTRL3D)
			{		
				HRESULT dsreturn = wQueryInterface(m_Buffer, IID_IDirectSound3DBuffer, (void **) &m_Buffer3D);
				if (dsreturn != DS_OK)
					STOP(( "Creation of 3D sound buffer failed" )); 
			}
		}
		m_oldProperties = m_currentProperties;
	}
}


void DS3DSoundChannel::Stop()
{
	gosASSERT(m_Buffer);
	playPending = false;
	DWORD dwStatus;
	wGetStatus(m_Buffer, &dwStatus);
	if ((dwStatus & DSBSTATUS_PLAYING) || m_fPaused)
	{
		wStop(m_Buffer);
		wSetCurrentPosition(m_Buffer, 0);
#ifdef LAB_ONLY
#ifndef _DEBUG
		if (DebuggerActive && DebugDisplay == DbgS_PlayHistory)
#endif
		{
			char tmp[256];
			if (m_currentSoundResource)
				sprintf(tmp, "%9.2f    Interrupt            %2d    <%s>\n",  gos_GetElapsedTime(), m_myEnum, m_resourceName[0] ? m_resourceName : "Unlabeled UserMemory");
			else
				sprintf(tmp, "%9.2f    Interrupt            %2d    <%s>\n",  gos_GetElapsedTime(), m_myEnum, m_resourceName );
			strncpy( SoundRenderer.m_playHistory[SoundRenderer.m_playHistoryItr], tmp, 127 );
			SoundRenderer.m_playHistoryItr++;
			if (SoundRenderer.m_playHistoryItr == 32)
				SoundRenderer.m_playHistoryItr = 0;
		}
#endif
	}
	m_fPlaying = FALSE;
	m_fPaused = FALSE;
}
void DS3DSoundChannel::Play()
{
	DWORD dwStatus;
	wGetStatus(m_Buffer, &dwStatus);
	
	if (!m_fPaused && !(dwStatus & DSBSTATUS_PLAYING) )
		wSetCurrentPosition(m_Buffer, 0);

	SetAllParameters();
	m_fPaused = FALSE;
	m_fPlaying = TRUE;

	SafeFPU();					// Make sure exceptions cannot be generated
	AllowFail = 1;
	HRESULT hr = wPlay(m_Buffer,0,0, m_bLoopFlag ? DSBPLAY_LOOPING : 0);
	//
	// If we got a WINERR_UnspecifiedError, there is precious little I can do about it...
	// ... but, until I think of something better, just trap and continue.
	//
	if (hr)
	{
		// special handling when i come up with a good solution...
		SPEW((GROUP_DIRECTSOUND, "Play returned \"%s\"", ErrorNumberToMessage(hr) ));
	}

	AllowFail = 0;
	GameOSFPU();				// Restore current GameOS FPU control word setup
}

void DS3DSoundChannel::PlayStream()
{
    if (m_Buffer)
    {
        // If playing, stop
        if (m_fPlaying)
            StopStream();

        // Cue for playback if necessary
        if (!m_fCued && !m_fPaused)
            Cue ();

		m_fPaused = FALSE;

		SetAllParameters();
        // Begin DirectSound playback
		SafeFPU();					// Make sure exceptions cannot be generated
        HRESULT hr = wPlay(m_Buffer, 0, 0, DSBPLAY_LOOPING);
		GameOSFPU();				// Restore current GameOS FPU control word setup
        if (hr == DS_OK)
        {
            // Kick off timer to service buffer, if one doesn't already exist.
            // Playback begun, no longer cued
            m_fPlaying = TRUE;
            m_fCued = FALSE;
        }
        else
			STOP(( "Playstream command failed" )); 
    }
	else
	{
		if (m_fPlaying && m_fPaused)
			gosMusic_SetPlayMode(m_currentSoundResource->m_hMusic, gosMusic_Continue);
		else
			gosMusic_SetPlayMode(m_currentSoundResource->m_hMusic, m_bLoopFlag ? gosMusic_Loop : gosMusic_PlayOnce);

        // Cue for playback if necessary
        if (!m_fCued && !m_fPaused)
            Cue ();

		m_fPaused = FALSE;

        m_fPlaying = TRUE;
        m_fCued = FALSE;
	}
}

void DS3DSoundChannel::CreateStreamBuffer( SoundResource* res )
{
	DSBUFFERDESC 	dsbd;

	ReleaseCurrent();
	bOnly2D = false;

	// by default!
	m_resourceType = res->m_Type;
	strncpy(m_resourceName, res->m_FileName,127);
	m_true3D = false;
	m_currentSoundResource = res;

	memset ( &dsbd, 0, sizeof( DSBUFFERDESC ) );
    dsbd.dwSize = sizeof(dsbd);

	// using the channel properties, create the initialization options for the buffer.
	if (m_currentProperties & gosAudio_Frequency)
		dsbd.dwFlags |= DSBCAPS_CTRLFREQUENCY;
	if ( m_currentProperties & gosAudio_Panning )
		dsbd.dwFlags |= DSBCAPS_CTRLPAN;


	// the mixer needs to control this, so add it by default!
	dsbd.dwFlags |= DSBCAPS_CTRLVOLUME;
	if (SoundRenderer.m_Mixer->m_dscaps.dwFlags & DSBCAPS_GETCURRENTPOSITION2)
		dsbd.dwFlags|= DSBCAPS_GETCURRENTPOSITION2 ;

	// prepare for 3d buffer if necessary
	if ( m_currentProperties & gosAudio_Position || m_currentProperties & gosAudio_Velocity || m_currentProperties & gosAudio_MinMaxDistance)
	{
#ifdef _DEBUG
		if (m_currentSoundResource->m_WaveFormatEx->nChannels > 1)
			SPEW((GROUP_DIRECTSOUND, "WASTING CPU: Playing stereo sound resource \"%s\"n in 3D", m_currentSoundResource->m_FileName));
#endif
		SoundRenderer.m_Mixer->GetCaps();
		if ( SoundRenderer.m_Mixer->m_dscaps.dwFreeHw3DAllBuffers && Environment.soundMixInHardware)
		{
			// for true positional sound
			dsbd.dwFlags |= DSBCAPS_CTRL3D;
			m_true3D = true;
		}
		else
		{
			// for fake positional sound
			dsbd.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_LOCSOFTWARE;
			dsbd.guid3DAlgorithm = DS3DALG_HRTF_LIGHT;
		}
	}

	if (m_currentSoundResource->m_WaveFormatEx->wFormatTag != 1)
	{
		HACMSTREAM hACMStream = NULL;

		wACMStreamOpen(&hACMStream,NULL,&m_currentSoundResource->m_suggFormat,&m_currentSoundResource->m_suggFormat,NULL,0,0,0);
		// we need to make sure that the buffersize jives with what ACM wants
		wACMStreamClose(hACMStream, 0);

		m_cbBufSize = m_currentSoundResource->m_suggFormat.nAvgBytesPerSec * 3;

		dsbd.lpwfxFormat = &m_currentSoundResource->m_suggFormat;
	}
	else
	{
		m_cbBufSize = m_currentSoundResource->m_WaveFormatEx->nAvgBytesPerSec * 3;

		if (m_cbBufSize > m_currentSoundResource->m_DataLength)
			m_cbBufSize = m_currentSoundResource->m_DataLength;
		dsbd.lpwfxFormat = m_currentSoundResource->m_WaveFormatEx;
	}

    // Get duration of sound
    m_fDuration = m_currentSoundResource->m_fDuration;

	dsbd.dwBufferBytes = m_cbBufSize;

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

	SafeFPU();					// Make sure exceptions cannot be generated
	wCreateSoundBuffer(SoundRenderer.m_Mixer->m_lpDirectSound, &dsbd, &m_Buffer, NULL);
	GameOSFPU();				// Restore current GameOS FPU control word setup

	SpewCaps();
	gos_ChangeHeapSize(SoundHeap, m_dsbCaps.dwBufferBytes, 1 );


	// create a 3d buffer if necessary
	if ( m_currentProperties & gosAudio_Position || m_currentProperties & gosAudio_Velocity || m_currentProperties & gosAudio_MinMaxDistance)
	{
		HRESULT dsreturn = wQueryInterface(m_Buffer, IID_IDirectSound3DBuffer, (void **) &m_Buffer3D);
		if (dsreturn != DS_OK)
			STOP(( "Creation of 3D sound buffer failed" )); 
	}

	SetVolume( m_fVolume );
	if (m_currentProperties & gosAudio_Frequency)
		SetFrequency( m_fFrequency );
	if (m_currentProperties & gosAudio_Panning)
		SetPan( m_fPanning );

	if ( m_currentProperties & gosAudio_MinMaxDistance )
		SetDistanceMinMax( m_Attributes3D.flMinDistance, m_Attributes3D.flMaxDistance );

	if (m_currentProperties & gosAudio_Position)
		SetPosition( m_Attributes3D.vPosition.x, m_Attributes3D.vPosition.y, m_Attributes3D.vPosition.z );

	if (m_currentProperties & gosAudio_Velocity)
		SetVelocity( m_Attributes3D.vVelocity.x, m_Attributes3D.vVelocity.y, m_Attributes3D.vVelocity.z );
	
	if (m_currentProperties & gosAudio_ConeOrientation)
		SetConeOrientation( m_Attributes3D.vConeOrientation.x, m_Attributes3D.vConeOrientation.y, m_Attributes3D.vConeOrientation.z );
	
	if (m_currentProperties & gosAudio_ConeAngles)
		SetConeAngles( m_Attributes3D.dwInsideConeAngle, m_Attributes3D.dwOutsideConeAngle );
	
}

void DS3DSoundChannel::Cue ()
{
    if (!m_fCued && m_resourceType != gosAudio_StreamedMusic)
    {
        // Reset buffer ptr
        m_cbBufOffset = 0;
		m_previousPlayPosition = 0;

        // Reset file ptr, etc
        m_currentSoundResource->Cue ();

        // Reset DirectSound buffer
		wSetCurrentPosition(m_Buffer, 0);

        // Fill initial buffer with wave data
        WriteWaveData(m_cbBufSize);
        
    }
    m_fCued = TRUE;
}


void DS3DSoundChannel::WriteWaveData (UINT size)
{
    LPBYTE lpbuf1 = NULL, lpbuf2 = NULL;
    DWORD dwsize1 = 0, dwsize2 = 0, dwbyteswritten1 = 0, dwbyteswritten2 = 0;

	//
	// First, lock the buffer. This will give us two stretches of buffer with which
	// to write new data into.
	//
	m_Buffer->Lock(m_cbBufOffset, size, (void **)&lpbuf1, &dwsize1, (void **)&lpbuf2, &dwsize2, 0);

	//
	// The first section of the buffer precedes the current play pointer. We can make the
	// most of this by writing into it.
	//
	if (lpbuf1)
	{
	    if (m_currentSoundResource->m_microsoftPCM)
			dwbyteswritten1 = 
				m_currentSoundResource->ReadPCM (lpbuf1, dwsize1, m_bLoopFlag );
		else
 			dwbyteswritten1 = 
				m_currentSoundResource->ReadACM (lpbuf1, dwsize1, m_bLoopFlag );
	}

	//
	// The second section of the buffer is beyond the scope of the section currently
	// being played. We're able to write here safely as well.
	//
	if (lpbuf2)  
	{
		if (m_currentSoundResource->m_microsoftPCM)
			dwbyteswritten2 = 
				m_currentSoundResource->ReadPCM (lpbuf2, dwsize2, m_bLoopFlag );
		else
			dwbyteswritten2 = 
				m_currentSoundResource->ReadACM (lpbuf2, dwsize2, m_bLoopFlag );
	}

	//
	// Of course, your going to need to unlock once you've locked and written.
	// We're done, so let's go ahead and do it.
	//
	if (dwbyteswritten1 == -1 || dwbyteswritten2 == -1)
	{
		m_Buffer->Unlock (lpbuf1, 0, lpbuf2, 0);
		StopStream();
		return;
	}
	else
	{
		m_Buffer->Unlock (lpbuf1, dwbyteswritten1, lpbuf2, dwbyteswritten2);
	}

	//
	// Now, we'll update some of our housekeeping variables:
	//   m_cbBufOffset is the next point at which we'll need to write data
	//	 m_nBytesPlayed will, however, help us determine how many bytes have played.
	//
	if (m_currentSoundResource->m_Type == gosAudio_StreamedFile || m_currentSoundResource->m_Type == gosAudio_StreamedFP)
	{
		m_cbBufOffset = (m_cbBufOffset + dwbyteswritten1 + dwbyteswritten2) % m_cbBufSize;
		m_Buffer->GetCurrentPosition( &m_playPosition, NULL);
		if (m_playPosition > m_previousPlayPosition)
			m_currentSoundResource->m_nBytesPlayed += m_playPosition - m_previousPlayPosition;
		else
		if (m_playPosition < m_previousPlayPosition)
			m_currentSoundResource->m_nBytesPlayed += m_playPosition + (m_cbBufSize - m_previousPlayPosition);
		m_previousPlayPosition = m_playPosition;
		if (m_bLoopFlag && m_currentSoundResource->m_nBytesPlayed > m_currentSoundResource->m_DataLength* (m_currentSoundResource->m_microsoftPCM ? 1 : 4))
			m_currentSoundResource->m_nBytesPlayed -= m_currentSoundResource->m_DataLength*(m_currentSoundResource->m_microsoftPCM ? 1 : 4);
	}
	else
	{
		m_cbBufOffset = (m_cbBufOffset + dwbyteswritten1 + dwbyteswritten2) % m_cbBufSize;
		m_Buffer->GetCurrentPosition( &m_playPosition, NULL);
		if (m_playPosition >= m_previousPlayPosition)
			m_currentSoundResource->m_nBytesPlayed += m_playPosition - m_previousPlayPosition;
		else
			m_currentSoundResource->m_nBytesPlayed += m_playPosition + (m_cbBufSize - m_previousPlayPosition);

		m_previousPlayPosition = m_playPosition;
	}
}

// Stop
void DS3DSoundChannel::StopStream()
{
	if (IsPlaying() || m_fPlaying)
    {
		if (m_Buffer)
		{
	        // Stop DirectSound playback
	        wStop(m_Buffer);
	        m_fPlaying = FALSE;
			m_currentSoundResource->m_nBytesPlayed = 0;
		}
		else
		{
			gosMusic_SetPlayMode(m_currentSoundResource->m_hMusic, gosMusic_Stop);
		}
#ifdef LAB_ONLY
#ifndef _DEBUG
		if (DebuggerActive && DebugDisplay == DbgS_PlayHistory)
#endif
		{
			char tmp[256];
			sprintf(tmp, "%9.2f    Interrupt            %2d    <%s>\n",  gos_GetElapsedTime(), m_myEnum, m_currentSoundResource->m_FileName ? m_currentSoundResource->m_FileName : "Unlabeled UserMemory");
			strncpy( SoundRenderer.m_playHistory[SoundRenderer.m_playHistoryItr], tmp, 127 );
			SoundRenderer.m_playHistoryItr++;
			if (SoundRenderer.m_playHistoryItr == 32)
				SoundRenderer.m_playHistoryItr = 0;
		}
#endif
    }
}

//
// ServicePlayList
//
// Routine to service buffer requests initiated by periodic timer.
//
// Returns TRUE if buffer serviced normally; otherwise returns FALSE.
BOOL DS3DSoundChannel::ServicePlayList ()
{
    DWORD dwFreeSpace = GetMaxWriteSize ();
	
	if (dwFreeSpace) 
		WriteWaveData (dwFreeSpace);

	float complete;
	if (m_currentSoundResource->m_Type == gosAudio_StreamedFile || m_currentSoundResource->m_Type == gosAudio_StreamedFP)
	{
		DWORD playpos;
		playpos = m_currentSoundResource->m_nBytesPlayed;
		complete = (float) m_currentSoundResource->m_nBytesPlayed / 
			((m_currentSoundResource->m_microsoftPCM ? 1 : 4) * (m_currentSoundResource->m_DataLength));
		if (complete >= 1.0f) 
		{
			if (m_bLoopFlag)
				m_currentSoundResource->m_nBytesPlayed = 
					m_currentSoundResource->m_nBytesPlayed - m_currentSoundResource->m_playListDataLength;
			else
				StopStream();
		}
	}
	else
	{
		if (m_currentSoundResource->m_microsoftPCM)
		{
			complete = ((float) m_currentSoundResource->m_nBytesPlayed / 
				m_currentSoundResource->m_playListDataLength) ;
		}
		else
		{
			complete = ((float) m_currentSoundResource->m_nBytesPlayed / 
				m_currentSoundResource->m_playListDataLength * 4) ;
		}
		if (complete >= 1.0f) 
		{
			if (m_bLoopFlag)
				m_currentSoundResource->m_nBytesPlayed = 
					m_currentSoundResource->m_nBytesPlayed - m_currentSoundResource->m_playListDataLength;
			else
				StopStream();
		}
	}
    return true;
}

// GetMaxWriteSize
//
// Helper function to calculate max size of sound buffer write operation, i.e. how much
// free space there is in buffer.
DWORD DS3DSoundChannel::GetMaxWriteSize ()
{
    DWORD dwWriteCursor, dwPlayCursor, dwMaxSize=0;

    // Get current play position
    if (m_Buffer->GetCurrentPosition(&dwPlayCursor, &dwWriteCursor) == DS_OK)
    {
        if (m_cbBufOffset <= dwPlayCursor)
        {
            // Our write position trails play cursor
            dwMaxSize = dwPlayCursor - m_cbBufOffset;
        }

        else // (m_cbBufOffset > dwPlayCursor)
        {
            // Play cursor has wrapped
            dwMaxSize = m_cbBufSize - m_cbBufOffset + dwPlayCursor;
        }
    }
    return (dwMaxSize);
}

void DS3DSoundChannel::Pause()
{
	m_fPaused = TRUE;
	wStop(m_Buffer);
}

void DS3DSoundChannel::PauseStream()
{
	m_fPaused = TRUE;
	if (m_resourceType == gosAudio_StreamedMusic)
	{
		gosMusic_SetPlayMode(m_currentSoundResource->m_hMusic, gosMusic_Pause);
	}
	else
		StopStream();
}

void DS3DSoundChannel::SetVolume(float v)
{
	if (v < 0.0f)
		v = 0.0f;
	else
	{
		if (v > 1.0f)
			v = 1.0f;
	}

	m_fVolume = v;
	m_changed = true;

	if (m_Buffer)
	{
		long Volume;
		if (m_fVolume == 0.0f || SoundRenderer.m_Mixer->m_fVolume == 0.0f)
			Volume = -10000;
		else
			Volume = (long)(log(m_fVolume * SoundRenderer.m_Mixer->m_fVolume)*1000.0f);

		if( Volume > -10000 )
				wSetVolume( m_Buffer, Volume );
			else
				wSetVolume( m_Buffer, -10000 );
	}
	if (m_resourceType == gosAudio_StreamedMusic)
	{
		gosMusic_Command(m_currentSoundResource->m_hMusic, gosMusic_Volume, v);
	}
}

void DS3DSoundChannel::SetPan(float v)
{
	gosASSERT( m_currentProperties & gosAudio_Panning );
	gosASSERT(v >= -1.0f && v <= 1.0f);

	if (v < -1.0f)
		v = 1.0f;
	else
	{
		if (v > 1.0f)
			v = 1.0f;
	}

	m_fPanning = v;
	m_changed = true;

	float through_mixer = m_fPanning;

	if (m_Buffer)
	{
		if (through_mixer != 0.0f)
		{
			if (through_mixer > 0.0f)
			{
				if (through_mixer >= .99f)
					wSetPan(m_Buffer, 10000);
				else
					wSetPan(m_Buffer, (LONG)((log(1.0f-through_mixer )*-1000.0f)));
			}
			else
			{
				if (through_mixer  <= -.99f)
					wSetPan(m_Buffer, -10000);
				else
					wSetPan(m_Buffer, (LONG)((log(1.0f+through_mixer )*1000.0f)));
			}
		}
		else
			wSetPan(m_Buffer, (LONG) 0);
	}
	if (m_resourceType == gosAudio_StreamedMusic)
	{
		gosMusic_Command(m_currentSoundResource->m_hMusic, gosMusic_Panning, v);
	}
}

void DS3DSoundChannel::SetFrequency(float v)
{
	gosASSERT( m_currentProperties & gosAudio_Frequency );
	gosASSERT(v > 0.0f);
	m_fFrequency = v;
	m_changed = true;

	if (m_currentSoundResource)
	{
		DWORD freq = m_currentSoundResource->m_WaveFormatEx->nSamplesPerSec;
		freq = (DWORD) ((float) freq * v);
		if (SoundRenderer.m_Mixer->m_dscaps.dwMaxSecondarySampleRate != 0)
		{
			if (freq > SoundRenderer.m_Mixer->m_dscaps.dwMaxSecondarySampleRate) 
				freq = SoundRenderer.m_Mixer->m_dscaps.dwMaxSecondarySampleRate;
			if (freq < SoundRenderer.m_Mixer->m_dscaps.dwMinSecondarySampleRate)
				freq = SoundRenderer.m_Mixer->m_dscaps.dwMinSecondarySampleRate;
		}
	 	wSetFrequency(m_Buffer, freq);
	}
}

void DS3DSoundChannel::SetPosition(float x, float y, float z)
{
	gosASSERT( m_currentProperties & gosAudio_Position );
	m_Attributes3D.vPosition.x = x;
	m_Attributes3D.vPosition.y = y;
	m_Attributes3D.vPosition.z = z;
	if (m_Buffer3D)
		wSetPosition(m_Buffer3D, x,y,z,DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundChannel::SetVelocity(float x, float y, float z)
{
	gosASSERT( m_currentProperties & gosAudio_Velocity );
	m_Attributes3D.vVelocity.x = x;
	m_Attributes3D.vVelocity.y = y;
	m_Attributes3D.vVelocity.z = z;
	if (m_Buffer3D)
		wSetVelocity(m_Buffer3D, x,y,z,DS3D_DEFERRED);

	m_changed = true;
}

void DS3DSoundChannel::SetDistanceMinMax(float min, float max)
{
	gosASSERT( m_currentProperties & gosAudio_MinMaxDistance );
	gosASSERT( min <= max );
	m_Attributes3D.flMinDistance = min;
	m_Attributes3D.flMaxDistance = max;
	if (m_Buffer3D)
	{
		wSetMinDistance(m_Buffer3D, min, DS3D_DEFERRED);
		wSetMaxDistance(m_Buffer3D, max, DS3D_DEFERRED);
		wSetPosition(m_Buffer3D, m_Attributes3D.vPosition.x, m_Attributes3D.vPosition.y, m_Attributes3D.vPosition.z, DS3D_DEFERRED);
	}
	m_changed = true;
}

bool DS3DSoundChannel::IsPlaying()
{
	DWORD dwStatus;
	if (m_resourceType == gosAudio_StreamedMusic)
	{
		gosMusic_PlayMode pm = gosMusic_GetPlayMode(m_currentSoundResource->m_hMusic);
		if (pm == gosMusic_Stop)
			return false;
		else
			return true;
	}
	else
	if (!m_Buffer)
		return false;
	wGetStatus(m_Buffer, &dwStatus);
	if (playPending)
		return true;
	return (dwStatus & DSBSTATUS_PLAYING);
}

DWORD DS3DSoundChannel::GetBytesPlayed()
{
	if (m_Buffer)
	{
		DWORD playpos;
		DWORD writepos;
		wGetCurrentPosition( m_Buffer, &playpos, &writepos );
		return playpos;
	}
	else
		return 0;
}

void DS3DSoundChannel::SpewCaps()
{
	if( m_Buffer )
	{
		m_dsbCaps.dwSize = sizeof( DSBCAPS );
		wGetCaps( m_Buffer, &m_dsbCaps );
	}
}

void DS3DSoundChannel::SetConeAngles( DWORD min, DWORD max )
{
	gosASSERT( m_currentProperties & gosAudio_ConeAngles );
	gosASSERT( min <= max );
	m_Attributes3D.dwInsideConeAngle = min;
	m_Attributes3D.dwOutsideConeAngle = max;
	wSetConeAngles(m_Buffer3D, min, max,  DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundChannel::SetConeOrientation( float x, float y, float z )
{
	gosASSERT( m_currentProperties & gosAudio_ConeOrientation );
	m_Attributes3D.vConeOrientation.x = x;
	m_Attributes3D.vConeOrientation.y = y;
	m_Attributes3D.vConeOrientation.z = z;
	if (m_Buffer3D)
		wSetConeOrientation(m_Buffer3D, x,y,z, DS3D_DEFERRED);
	m_changed = true;
}

void DS3DSoundChannel::Reset(DWORD channel)
{
	if (m_Buffer)
	{
		// null the buffers, they're already released when the mixer is rebuilt....
		m_Buffer3D = NULL;
		m_Buffer = NULL;
		gos_ChangeHeapSize(SoundHeap, -((signed) m_dsbCaps.dwBufferBytes ), 1 );
	}

	if (!SoundRenderer.m_isInitialized)
		return;

	// if the channel has no assigned resources, don't do anything
	if ( !m_currentSoundResource )
		return;
	else
	{
		HGOSAUDIO tmp = m_currentSoundResource;
		m_currentSoundResource = NULL;
		gosAudio_AssignResourceToChannel(channel, tmp);
	}


	if (m_bLoopFlag && !m_fPaused && m_fPlaying)
		gosAudio_SetChannelPlayMode(channel, gosAudio_Loop);
	else
	if (!m_bLoopFlag && !m_fPaused && m_fPlaying)
		gosAudio_SetChannelPlayMode(channel, gosAudio_PlayOnce);
	else
	if (!m_fPlaying && !m_fPaused)
		gosAudio_SetChannelPlayMode(channel, gosAudio_Stop);
	else
	if (!m_fPlaying && m_fPaused)
		gosAudio_SetChannelPlayMode(channel, gosAudio_Pause);
}

void DS3DSoundChannel::SetAllParameters()
{
	//
	// This function is called when it is very important that the sound be spatialized immediately...
	// ... as in the case of a play command.
	//
	if (m_Buffer3D)
	{
		wSetAllParameters(m_Buffer3D, &m_Attributes3D, DS3D_DEFERRED);
		m_changed = true;
	}
}

void DS3DSoundChannel::ReleaseCurrent()
{
	if (m_Buffer)
	{
		// if there is a sound buffer associated with the channel, stop it
		wStop(m_Buffer);
		
		// release the buffer
		if (m_Buffer3D)
			wRelease(m_Buffer3D);

		if ( !bIsDuplicate )
		{
			SPEW((GROUP_DIRECTSOUND, "Releasing a non-duplicate buffer (0x%x)", m_Buffer));
			wRelease(m_Buffer);
			if (m_currentSoundResource)
			{
				if ( !(m_resourceType == gosAudio_CachedFile || m_resourceType == gosAudio_UserMemory ) && !bOnly2D)
					gos_ChangeHeapSize(SoundHeap, -((signed int) m_dsbCaps.dwBufferBytes), 1 );
			}
		}
		else
		{
			if ( m_resourceType == gosAudio_CachedFile || m_resourceType == gosAudio_UserMemory ) 
			{
				if ( m_currentSoundResource )
				{
					m_currentSoundResource->RelinquishDuplicate(m_Buffer);
				}
				else
				{
					SPEW((GROUP_DIRECTSOUND, "Releasing a duplicate buffer (0x%x)", m_Buffer));
					wRelease(m_Buffer);
				}
			}
		}
	}
	m_Buffer3D = NULL;
	m_Buffer = NULL;
	bOnly2D = false;
	m_true3D = false;
}