//============================================================================
//
//   SSSS    tt          lll  lll       
//  SS  SS   tt           ll   ll        
//  SS     tttttt  eeee   ll   ll   aaaa 
//   SSSS    tt   ee  ee  ll   ll      aa
//      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
//  SS  SS   tt   ee      ll   ll  aa  aa
//   SSSS     ttt  eeeee llll llll  aaaaa
//
// Copyright (c) 1995-1998 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SndXBOX.cxx,v 1.1.1.1 2001/12/27 19:54:32 bwmott Exp $
//============================================================================


#include "SndXBOX.hxx"
#include <stdio.h>
#include <stdlib.h>
#include "bochs.h"

#ifdef __cplusplus
extern "C" {
#endif



#ifdef __cplusplus
}
#endif

// the local buffer is what the stream buffer feeds from
// note that this needs to be large enough to buffer at frameskip 11
// for 30fps games like Tapper; we will scale the value down based
// on the actual framerate of the game
#define MAX_BUFFER_SIZE			(128 * 1024)

// this is the maximum number of extra samples we will ask for
// per frame (I know this looks like a lot, but most of the
// time it will generally be nowhere close to this)
#define MAX_SAMPLE_ADJUST		16

extern DWORD g_dwStartTime ;
extern DWORD g_dwTimePaused ;

/**
  Compute the buffer size to use based on the given sample rate

  @param The sample rate to compute the buffer size for
*/
static unsigned long computeBufferSize(int sampleRate)
{
  int t;

  for(t = 7; t <= 12; ++t)
  {
    if((1 << t) > (sampleRate / 60))
    {
      return (1 << (t - 1));
    }
  }

  return 256;
}

//FILE *sout ;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundXBOX::SoundXBOX()
{
	dsound = NULL ;
	stream_buffer = NULL ;
	stream_buffer_spk = NULL ;
	attenuation = 0 ;
	samples_to_read = 0 ;
	m_fps = 60 ;

	//sout = fopen( "d:\\bochs.raw", "wb" ) ;

}
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundXBOX::~SoundXBOX()
{
}

int SoundXBOX::dsound_init(void)
{
	HRESULT result;


	current_adjustment = 0 ;
	m_totalBytesWrittenSpk = 0 ;

	// make a format description for what we want
	stream_format.wBitsPerSample	= 8;
	stream_format.wFormatTag		= WAVE_FORMAT_PCM;
	stream_format.nChannels			= 1;
	stream_format.nSamplesPerSec	= 44100;
	stream_format.nBlockAlign		= stream_format.wBitsPerSample * stream_format.nChannels / 8;
	stream_format.nAvgBytesPerSec	= stream_format.nSamplesPerSec * stream_format.nBlockAlign;

	// compute the buffer sizes
	stream_buffer_size_spk = (UINT64)MAX_BUFFER_SIZE ;
//	stream_buffer_size_spk = ((UINT64)MAX_BUFFER_SIZE * (UINT64)stream_format.nSamplesPerSec) / 44100;
	stream_buffer_size_spk = (stream_buffer_size_spk * stream_format.nBlockAlign) / 4;
	stream_buffer_size_spk = (stream_buffer_size_spk * 30) / 60 ;
	stream_buffer_size_spk = (stream_buffer_size_spk / 1024) * 1024;

	stream_buffer_in = 0 ;

	DSMIXBINVOLUMEPAIR dsmbvp[8] = {
		{DSMIXBIN_FRONT_LEFT, DSBVOLUME_MAX},   // left channel
		{DSMIXBIN_FRONT_RIGHT, DSBVOLUME_MAX},  // right channel
		{DSMIXBIN_FRONT_CENTER, DSBVOLUME_MAX}, // left channel
		{DSMIXBIN_FRONT_CENTER, DSBVOLUME_MAX}, // right channel
		{DSMIXBIN_BACK_LEFT, DSBVOLUME_MAX},    // left channel
		{DSMIXBIN_BACK_RIGHT, DSBVOLUME_MAX},   // right channel
		{DSMIXBIN_LOW_FREQUENCY, DSBVOLUME_MAX},    // left channel
		{DSMIXBIN_LOW_FREQUENCY, DSBVOLUME_MAX}};   // right channel
		
	DSMIXBINS dsmb;
	dsmb.dwMixBinCount = 8;
	dsmb.lpMixBinVolumePairs = dsmbvp;
	
	// create a buffer desc for the stream buffer
	stream_desc.dwSize				= sizeof(stream_desc);
	stream_desc.dwFlags				= 0 ;
	stream_desc.dwBufferBytes 		= 0; //we'll specify our own data
//	stream_desc.dwBufferBytes 		= stream_buffer_size;
	stream_desc.lpwfxFormat			= &stream_format;
	stream_desc.lpMixBins			= &dsmb;
	
	// create the stream buffer
	if ((result = dsound->CreateSoundBuffer(&stream_desc, &stream_buffer_spk, NULL)) != DS_OK)
	{
		stream_buffer_spk = NULL ;
		return 1 ;
	}
	
	m_pSoundBufferDataSpk = (byte*)malloc( stream_buffer_size_spk ) ;

	stream_buffer_spk->SetBufferData( m_pSoundBufferDataSpk, stream_buffer_size_spk ) ;
	stream_buffer_spk->SetPlayRegion( 0, stream_buffer_size_spk ) ;
	stream_buffer_spk->SetLoopRegion( 0, stream_buffer_size_spk ) ;

	memset( m_pSoundBufferDataSpk, 0, stream_buffer_size_spk);
	
	stream_buffer_spk->SetVolume( DSBVOLUME_MAX );
	stream_buffer_spk->SetCurrentPosition( 0 ) ;

	m_dwWritePosSpk = 0 ;
	return 0 ;
}


void SoundXBOX::init( )
{
	m_totalBytesWrittenSpk = 0 ;
	m_dwWritePosSpk = 0 ;
	memset( m_pSoundBufferDataSpk, 0, stream_buffer_size_spk ) ;
	stream_buffer_spk->SetVolume( DSBVOLUME_MAX );
	stream_buffer_spk->Stop() ;
	stream_buffer_spk->SetCurrentPosition( 0 ) ;
}

int SoundXBOX::setupSB(int frequency, int bits, int stereo)
{
	HRESULT result;


	if ( stream_buffer != NULL )
	{
		IDirectSoundBuffer_Stop(stream_buffer);
		IDirectSoundBuffer_Release(stream_buffer);
		stream_buffer = NULL;
	}

	current_adjustment = 0 ;
	m_totalBytesWritten = 0 ;

	// make a format description for what we want
	stream_format.wBitsPerSample	= bits;
	stream_format.wFormatTag		= WAVE_FORMAT_PCM;
	stream_format.nChannels			= 1+stereo;
	stream_format.nSamplesPerSec	= frequency;
	stream_format.nBlockAlign		= stream_format.wBitsPerSample * stream_format.nChannels / 8;
	stream_format.nAvgBytesPerSec	= stream_format.nSamplesPerSec * stream_format.nBlockAlign;

	// compute the buffer sizes
	stream_buffer_size = (UINT64)MAX_BUFFER_SIZE ;
//	stream_buffer_size = ((UINT64)MAX_BUFFER_SIZE * (UINT64)stream_format.nSamplesPerSec) / 44100;
	stream_buffer_size = (stream_buffer_size * stream_format.nBlockAlign) / 4;
	stream_buffer_size = (stream_buffer_size * 30) / 60 ;
	stream_buffer_size = (stream_buffer_size / 1024) * 1024;

	stream_buffer_in = 0 ;

	DSMIXBINVOLUMEPAIR dsmbvp[8] = {
		{DSMIXBIN_FRONT_LEFT, DSBVOLUME_MAX},   // left channel
		{DSMIXBIN_FRONT_RIGHT, DSBVOLUME_MAX},  // right channel
		{DSMIXBIN_FRONT_CENTER, DSBVOLUME_MAX}, // left channel
		{DSMIXBIN_FRONT_CENTER, DSBVOLUME_MAX}, // right channel
		{DSMIXBIN_BACK_LEFT, DSBVOLUME_MAX},    // left channel
		{DSMIXBIN_BACK_RIGHT, DSBVOLUME_MAX},   // right channel
		{DSMIXBIN_LOW_FREQUENCY, DSBVOLUME_MAX},    // left channel
		{DSMIXBIN_LOW_FREQUENCY, DSBVOLUME_MAX}};   // right channel
		
	DSMIXBINS dsmb;
	dsmb.dwMixBinCount = 8;
	dsmb.lpMixBinVolumePairs = dsmbvp;
	
	// create a buffer desc for the stream buffer
	stream_desc.dwSize				= sizeof(stream_desc);
	stream_desc.dwFlags				= 0 ;
	stream_desc.dwBufferBytes 		= 0; //we'll specify our own data
//	stream_desc.dwBufferBytes 		= stream_buffer_size;
	stream_desc.lpwfxFormat			= &stream_format;
	stream_desc.lpMixBins			= &dsmb;
	
	// create the stream buffer
	if ((result = dsound->CreateSoundBuffer(&stream_desc, &stream_buffer, NULL)) != DS_OK)
	{
		stream_format.nSamplesPerSec	= (frequency/11025) * 11025 ;
		if ( stream_format.nSamplesPerSec == 0 )
			stream_format.nSamplesPerSec = 11025 ;

		if ( stream_format.nSamplesPerSec > 44100 )
			stream_format.nSamplesPerSec = 44100 ;

		stream_buffer_size = (UINT64)MAX_BUFFER_SIZE ;
	//	stream_buffer_size = ((UINT64)MAX_BUFFER_SIZE * (UINT64)stream_format.nSamplesPerSec) / 44100;
		stream_buffer_size = (stream_buffer_size * stream_format.nBlockAlign) / 4;
		stream_buffer_size = (stream_buffer_size * 30) / 60 ;
		stream_buffer_size = (stream_buffer_size / 1024) * 1024;

		if ((result = dsound->CreateSoundBuffer(&stream_desc, &stream_buffer, NULL)) != DS_OK)
		{
			stream_format.wBitsPerSample	= 8;
			stream_format.wFormatTag		= WAVE_FORMAT_PCM;
			stream_format.nChannels			= 1;
			stream_format.nSamplesPerSec	= 22050;
			stream_buffer_size = (UINT64)MAX_BUFFER_SIZE ;
		//	stream_buffer_size = ((UINT64)MAX_BUFFER_SIZE * (UINT64)stream_format.nSamplesPerSec) / 44100;
			stream_buffer_size = (stream_buffer_size * stream_format.nBlockAlign) / 4;
			stream_buffer_size = (stream_buffer_size * 30) / 60 ;
			stream_buffer_size = (stream_buffer_size / 1024) * 1024;

			if ((result = dsound->CreateSoundBuffer(&stream_desc, &stream_buffer, NULL)) != DS_OK)
			{
				stream_buffer = NULL ;
				return 0 ;
			}
		}

	}
	
	m_pSoundBufferData = (byte*)malloc( stream_buffer_size ) ;

	stream_buffer->SetBufferData( m_pSoundBufferData, stream_buffer_size ) ;
	stream_buffer->SetPlayRegion( 0, stream_buffer_size ) ;
	stream_buffer->SetLoopRegion( 0, stream_buffer_size ) ;

	memset( m_pSoundBufferData, 0, stream_buffer_size);
	
	stream_buffer->SetVolume( DSBVOLUME_MAX );
	stream_buffer->SetCurrentPosition( 0 ) ;

	m_dwWritePos = 0 ;
	return 1 ;
}


void SoundXBOX::initSB( )
{
	m_totalBytesWrittenSpk = 0 ;
	m_dwWritePosSpk = 0 ;
	memset( m_pSoundBufferDataSpk, 0, stream_buffer_size_spk ) ;
	stream_buffer_spk->SetVolume( DSBVOLUME_MAX );
	stream_buffer_spk->Stop() ;
	stream_buffer_spk->SetCurrentPosition( 0 ) ;
}


void SoundXBOX::cleanup()
{
	stream_buffer_spk->SetVolume( DSBVOLUME_MIN );
	//stream_buffer_spk->Stop() ;
}

void SoundXBOX::processSB( unsigned char *sbdata, UINT32 numbytes )
{
	unsigned char *sndbuf ;
	int            num_written ;
	float          numtowrite_f ;
	int            numtowrite ;
	float          waittime ;
	DWORD          waittime_ms ;
	float          elapsedTime ;
	DWORD          playPos  ;
	DWORD          bytesToEnd ;
	DWORD          distWritePlay ;
	DWORD          newWritePos ;
	DWORD          status ;


	BX_INFO( ( "process %u sound bytes\r\n", numbytes ) ) ;
	do
	{
		stream_buffer->GetStatus( &status) ;
	}
	while ( status & DSBSTATUS_PLAYING ) ;

	memcpy( m_pSoundBufferData, sbdata, numbytes ) ;
	stream_buffer->SetPlayRegion( 0, numbytes) ;
	stream_buffer->Play( 0, 0, DSBPLAY_FROMSTART ) ;

	//elapsedTime = ( (float)GetTickCount() - (float)g_dwStartTime - (float)g_dwTimePaused ) / 1000.0f ;

	//#ms to wait to allow soundbuffer to catch up staying within 5000bytes of the currpos
	//waittime = ( ( ( ((float)m_totalBytesWritten) + 5000.0f ) / 88200.0f ) - elapsedTime ) * 1000.0f;

	//if ( waittime > 0.0f )
	//{
		//waittime_ms = waittime ;
		//Sleep( waittime_ms ) ;
	//}
/*
	if ( pcm.pos > 0 )
	{

		bytesToEnd = stream_buffer_size - m_dwWritePos ;

		if ( pcm.pos > bytesToEnd )
		{
			memcpy( m_pSoundBufferData + m_dwWritePos, pcm.buf, bytesToEnd ) ;
			memcpy( m_pSoundBufferData, pcm.buf + bytesToEnd, pcm.pos - bytesToEnd ) ;
		}
		else
		{
			memcpy( m_pSoundBufferData + m_dwWritePos, pcm.buf, pcm.pos ) ;
		}

		m_dwWritePos = ( m_dwWritePos + pcm.pos ) % stream_buffer_size ;

		m_totalBytesWritten += pcm.pos ;

		do
		{
			stream_buffer->GetCurrentPosition( &playPos, NULL ) ;

			if ( m_dwWritePos > playPos )
			{
				distWritePlay = m_dwWritePos - playPos ;
			}
			else
			{
				distWritePlay = ( stream_buffer_size - playPos ) + m_dwWritePos ;
			}
		} while ( distWritePlay > 5000 ) ;


		pcm.pos = 0 ;
	}
*/

}

void SoundXBOX::writeSpeaker( unsigned char *spkbuf, UINT32 numbytes)
{
	unsigned char *sndbuf ;
	int            num_written ;
	float          numtowrite_f ;
	int            numtowrite ;
	float          waittime ;
	DWORD          waittime_ms ;
	float          elapsedTime ;
	DWORD          playPos  ;
	DWORD          bytesToEnd ;
	DWORD          distWritePlay ;
	DWORD          newWritePos ;


	//elapsedTime = ( (float)GetTickCount() - (float)g_dwStartTime - (float)g_dwTimePaused ) / 1000.0f ;

	//#ms to wait to allow soundbuffer to catch up staying within 5000bytes of the currpos
	//waittime = ( ( ( ((float)m_totalBytesWritten) + 5000.0f ) / 88200.0f ) - elapsedTime ) * 1000.0f;

	//if ( waittime > 0.0f )
	//{
		//waittime_ms = waittime ;
		//Sleep( waittime_ms ) ;
	//}


		bytesToEnd = stream_buffer_size_spk - m_dwWritePosSpk ;

		if ( numbytes > bytesToEnd )
		{
			memcpy( m_pSoundBufferDataSpk + m_dwWritePosSpk, spkbuf, bytesToEnd ) ;
			memcpy( m_pSoundBufferDataSpk, spkbuf + bytesToEnd, numbytes - bytesToEnd ) ;
		}
		else
		{
			memcpy( m_pSoundBufferDataSpk + m_dwWritePosSpk, spkbuf, numbytes ) ;
		}

		m_dwWritePosSpk = ( m_dwWritePosSpk + numbytes ) % stream_buffer_size_spk ;

		m_totalBytesWrittenSpk += numbytes ;
/*
		do
		{
			stream_buffer_spk->GetCurrentPosition( &playPos, NULL ) ;

			if ( m_dwWritePosSpk > playPos )
			{
				distWritePlay = m_dwWritePosSpk - playPos ;
			}
			else
			{
				distWritePlay = ( stream_buffer_size_spk - playPos ) + m_dwWritePosSpk ;
			}
		} while ( distWritePlay > 5000 ) ;
*/

	//fwrite( spkbuf, 1, numbytes, sout ) ;
	//fflush(sout) ;

}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundXBOX::pause(bool state)
{

	if (stream_buffer_spk)
	{
		if (state)
			stream_buffer_spk->Stop() ;
		else
			stream_buffer_spk->Play( 0, 0, DSBPLAY_LOOPING ) ;
	}
}

void SoundXBOX::stopSB()
{

	if (stream_buffer)
	{
		stream_buffer->Stop() ;
	}
}
