// ------------------------------------------------------------------------------------------------
// main.c
//
// This demonstrates the playing of a stereo stream from memory, playing a mono stream is the
// same but it is not interleaved.
//
// COPYRIGHT (C) SEGA OF AMERICA, INC. 1998
// All rights reserved.
//
// Information and source code samples contained herein are 
// provided "as-is", without representations or warranties, and 
// are subject to change without notice.
//
// SEGA OF AMERICA, INC. cannot support modifications or 
// derivative works created from the sample source code provided. 
// Developers may use, reproduce, and modify portions or entire 
// sections of the sample source code for the purposes of creating
// Dreamcast applications.  
//
// Sample source code contained herein may not be appropriate 
// for use under all circumstances.  Please consult Developer 
// documentation provided along with your Dreamcast unit for
// additional information and suggestions.
//
//
// Author: Ralph "Cooksey" Thomas
//
// Tabs: 4
//
// Rev:			04/05/99	File Originated
// ------------------------------------------------------------------------------------------------

#include <shinobi.h>
#include <sg_chain.h>
#include <am.h>
#include "MyStream.h"
#include "MyFile.h"
#include "MyInt.h"
#include "MyPrintf.h"
#include "MyButton.h"
#include "MyMalloc.h"
#include "MyAmSetup.h"

// ------------------------------------------------------------------------------------------------
// constants
// ------------------------------------------------------------------------------------------------

// #define PLAY_MONO	// This will cause the sample to play a mono stream, else interleaved stereo...

#ifdef PLAY_MONO
#define MONO_FILE_NAME						"M_Hip101.pcm" // mono 16 bit 44.1k pcm (raw) format file 
#define STREAM_TEST_PLAY_BUFFER_SIZE		16384
#else
#define LEFT_FILE_NAME						"L_Hip101.pcm" // mono 16 bit 44.1k pcm (raw) format file 
#define RIGHT_FILE_NAME						"R_Hip101.pcm" // mono 16 bit 44.1k pcm (raw) format file 
#define STREAM_TEST_PLAY_BUFFER_SIZE		16384
#endif

// ------------------------------------------------------------------------------------------------
// proto's
// ------------------------------------------------------------------------------------------------

KTBOOL MemoryStreamSetup(void);

// ------------------------------------------------------------------------------------------------
// globals
// ------------------------------------------------------------------------------------------------

KTU32 				gMainBufferSize						=	0;
KTU8 *				gMainBuffer							=	KTNULL;
KTU8 *				gLeftBuffer							=	KTNULL;
KTU8 *				gRightBuffer						=	KTNULL;
KTU8 *				gMonoBuffer							=	KTNULL;
KTU32				gLeftBufferSize						=	0;
KTU32				gMonoBufferSize						=	0;
KTU32				gRightBufferSize					=	0;
KTU32				gDataSize							=	0;
char *				gTarget								=	KTNULL;
char *				gSource								=	KTNULL;	
AM_STREAM_PTR		gStream								=	KTNULL;
KTU8				gStreamVolume						=	AM_MAX_VOLUME; // AM_MAX_VOLUME = 127
volatile KTU32 *	gPlayBuffer							=	KTNULL;

// ------------------------------------------------------------------------------------------------
// code
// ------------------------------------------------------------------------------------------------

#ifdef __GNUC__
int main(void)
#else
void main(void)
#endif
{
	SYE_CBL_CABLE Cable;
	PDS_PERIPHERAL *	pad;
	KTU32				j								=	0;
	char 				streamMessage[80]				= {"Playing"};

#ifdef __GNUC__
	shinobi_workaround();
#endif	

	njSetBorderColor(0x00000000);
	
	// Check the cable for NTSC or VGA 
	Cable = syCblCheckCable();
	
	switch( Cable )
		{
		case SYE_CBL_CABLE_VGA:
			sbInitSystem(NJD_RESOLUTION_VGA, NJD_FRAMEBUFFER_MODE_RGB565, 1);
			break;
	
		case SYE_CBL_CABLE_NTSC:
			sbInitSystem(NJD_RESOLUTION_640x480_NTSCNI, NJD_FRAMEBUFFER_MODE_RGB565, 1);
			break;
		}
	
	njInitVertexBuffer( 20000, 0, 20000, 0, 0 );
	njInitPrint( NULL, 0, NJD_TEXFMT_ARGB_1555 );
	njSetBackColor( 0x00000000, 0x00000000, 0x000000FF );
	njPrintSize(12);
	
	// ----------------------------------------------------------------------------------------------
	// Initialize the am library for interrupt messaging
	// ----------------------------------------------------------------------------------------------

	if(MyAmSetup(KTFALSE)==KTFALSE)
		{
		MyDebugf("MyAmSetup() FAIL");
		return;
		}

	// ----------------------------------------------------------------------------------------------
	// Setup a memory stream
	// ----------------------------------------------------------------------------------------------
	
	if(MemoryStreamSetup()==KTFALSE)
		{
		MyDebugf("MemoryStreamSetup() FAIL");
		return;
		}
	
	// fry er up...		

	// ----------------------------------------------------------------------------------------------
	// Start the buffer playing
	// ----------------------------------------------------------------------------------------------

	MyStreamStart(gStream);
		
	while(1)
		{
		pad = (PDS_PERIPHERAL *)pdGetPeripheral(PDD_PORT_A0);
	
	
		MyDcPrintf(INFO_1_X,INFO_1_Y,"Interrupts to end: %u Interrupt counter: %u",gStream->interruptsTillEnd,gStream->isrCounter);
	
		MyDcPrintf(INFO_3_X,INFO_3_Y,"Press the D-Pad up/down to change stream volume");
		MyDcPrintf(INFO_4_X,INFO_4_Y,"Press the D-Pad left/right to stop/start stream");
	
		MyDcPrintf(STR_STATUS_X,STR_STATUS_Y,"Stream Status: %s",streamMessage);
		MyDcPrintf(STR_VOL_X,STR_VOL_Y,"Stream Volume: %u",gStreamVolume);
	
		MyDcPrintf(AM_ERROR_X,AM_ERROR_Y,"Am Error Number: %d",gMyAmError->number);
		MyDcPrintf(AC_ERROR_X,AC_ERROR_Y,"Ac Error Number: %d",gMyAcError->number);
	
		MyStreamServer();
	
		if(amErrorExists()==KTTRUE)
			{
			gMyAmError	=	amErrorGetLast();
			}
	
		if(acErrorExists()==KTTRUE)
			{
			gMyAcError	=	acErrorGetLast();
			}
	
		if(MyStreamIsPlaying(gStream)==KTFALSE)
			{
			strcpy(streamMessage,"Done");
			}
	
	
		if(pad->press & PAD_MOVE_UP)
			{
			++gStreamVolume;
			
			if(gStreamVolume > AM_MAX_VOLUME)
				gStreamVolume = AM_MAX_VOLUME;
				
			MyStreamSetVolume(gStream,gStreamVolume);			 
			}
		else if(pad->press & PAD_MOVE_DOWN)
			{
			--gStreamVolume;
			
			if(gStreamVolume > AM_MAX_VOLUME) // its unsigned, underflow is positive
				gStreamVolume = 0;
	
			MyStreamSetVolume(gStream,gStreamVolume);			
			}
		else if(pad->press & PAD_MOVE_LEFT)
			{
			MyStreamDestroy(gStream);
			strcpy(streamMessage,"Stopped");
			}
		else if(pad->press & PAD_MOVE_RIGHT)
			{
			if(MyStreamIsPlaying(gStream)==KTFALSE)
				{
				MyStreamDestroyBuffer(gStream);

				#ifdef PLAY_MONO				
				if((gStream=MyStreamPrepareBuffer((volatile KTU32 *)gMonoBuffer,gMonoBufferSize,gPlayBuffer,gStreamVolume,64,44100,16,AM_MONO,KTNULL))==KTNULL)
					{
					MyDebugf("MyStreamPrepareFile() FAIL on restart");
					return;
					}
				#else
				if((gStream=MyStreamPrepareBuffer((volatile KTU32 *)gMainBuffer,gMainBufferSize,gPlayBuffer,gStreamVolume,64,44100,16,AM_STEREO,KTNULL))==KTNULL)
					{
					MyDebugf("MyStreamPrepareFile() FAIL on restart");
					return;
					}
				#endif

				MyStreamStart(gStream);

				strcpy(streamMessage,"Playing");
				}
			}
	
		njWaitVSync();
		}
	
	
	// ----------------------------------------------------------------------------------------------
	// Shutdown the am library
	// ----------------------------------------------------------------------------------------------
	
	amShutdown();
}


KTBOOL MemoryStreamSetup(void)
{
	KTU32 interleaveRate	=	STREAM_TEST_PLAY_BUFFER_SIZE/2;	
	KTU32 writePosition		=	0;
	KTU32 readPosition		=	0;

	// ----------------------------------------------------------------------------------------------
	// Set the default buffer sizes for the stream
	// ----------------------------------------------------------------------------------------------

	MyStreamSetPlayBufferSize(STREAM_TEST_PLAY_BUFFER_SIZE);


#ifdef PLAY_MONO
	// ----------------------------------------------------------------------------------------------
	// Set up the memory stream
	// ----------------------------------------------------------------------------------------------

	MyDebugf("Loading audio data, this will take a second or so...");
			
	// get the size of the left asset
	if(amFileGetSize(MONO_FILE_NAME,&gMonoBufferSize)==KTFALSE)
		return KTFALSE;

	// make the main buffer size be a multiple of the interleave rate
	if(gMonoBufferSize % interleaveRate)
		{
		gMonoBufferSize += (interleaveRate - (gMonoBufferSize % interleaveRate));
		}
	
	// get a buffer in sh4 memory for the file
	if((gMonoBuffer=syMalloc(gMonoBufferSize))==NULL)
		return KTFALSE;
	
	// fill with silence
	memset(gMonoBuffer,0x00,gMonoBufferSize);


	MyDebugf("Loading asset");

	// load the left asset into the buffer
	if(amFileLoad(MONO_FILE_NAME,(KTU8 *)gMonoBuffer)==KTFALSE)
		return KTFALSE;
		
	// ------------------------------------------------------------------------------------------------------
	// allocate a play buffer in sound memory (note the buffer required is (channels * PLAY_BUFFER_SIZE)
	// ------------------------------------------------------------------------------------------------------

	if(amHeapAlloc(&gPlayBuffer,STREAM_TEST_PLAY_BUFFER_SIZE,32,AM_PURGABLE_MEMORY,KTNULL)==KTFALSE)
		{
		MyDebugf("amHeapAlloc(0x%x,%u,%u,AM_PURGABLE_MEMORY,KTNULL) FAIL\n",gPlayBuffer,STREAM_TEST_PLAY_BUFFER_SIZE,32);
		return KTFALSE;
		}
	else
		MyDebugf("amHeapAlloc(0x%x,%u,%u,AM_PURGABLE_MEMORY,KTNULL)\n",gPlayBuffer,STREAM_TEST_PLAY_BUFFER_SIZE,32);

	// ------------------------------------------------------------------------------------------------------
	// allocate a play buffer in sound memory
	// ------------------------------------------------------------------------------------------------------

	gStream	=	 MyStreamPrepareBuffer((volatile KTU32 *)gMonoBuffer,	
														gMonoBufferSize,
														gPlayBuffer,
														gStreamVolume,
														64,
														44100,
														16,
														AM_MONO,
														KTNULL);

	// ----------------------------------------------------------------------------------------------
	// end of #ifdef PLAY_MONO
	// ----------------------------------------------------------------------------------------------
	
#else
	// ----------------------------------------------------------------------------------------------
	// Set up the memory stream
	// ----------------------------------------------------------------------------------------------

	MyDebugf("Loading audio data, this will take a second or so...");
			
	// get the size of the left asset
	if(amFileGetSize(LEFT_FILE_NAME,&gLeftBufferSize)==KTFALSE)
		return KTFALSE;
	
	// get the size of the right asset
	if(amFileGetSize(RIGHT_FILE_NAME,&gRightBufferSize)==KTFALSE)
		return KTFALSE;
	
	// get a buffer in sh4 memory for the left file
	if((gLeftBuffer=syMalloc(gLeftBufferSize))==NULL)
		return KTFALSE;
	
	// get a buffer in sh4 memory for the right file
	if((gRightBuffer=syMalloc(gRightBufferSize))==NULL)
		return KTFALSE;
	
	MyDebugf("Loading left asset");

	// load the left asset into the buffer
	if(amFileLoad(LEFT_FILE_NAME,(KTU8 *)gLeftBuffer)==KTFALSE)
		return KTFALSE;
	
	MyDebugf("Loading right asset");

	// load the right asset into the buffer
	if(amFileLoad(RIGHT_FILE_NAME,(KTU8 *)gRightBuffer)==KTFALSE)
		return KTFALSE;
	
	// set the size of the main buffer
	gMainBufferSize	=	gDataSize	=	gLeftBufferSize + gRightBufferSize;
	
	// make the main buffer size be a multiple of the interleave rate
	if(gMainBufferSize % interleaveRate)
		{
		gMainBufferSize += (interleaveRate - (gMainBufferSize % interleaveRate));
		}
		
	// get the main buffer in sh4 memory, this is the buffer from which the sound is streamed.
	if((gMainBuffer=syMalloc(gMainBufferSize))==NULL)
		return KTFALSE;
			
	// fill the main buffer with silence
	memset(gMainBuffer,0x00,gMainBufferSize);
	
	// ------------------------------------------------------------------------------------------------------
	// interleave the data
	//
	// The interleave rate is 1/2 of the size of the play buffer.
	//
	// For stereo buffer playback the data needs to be interleaved in chunks half the size of the play 
	// buffer, left channel first then right. If the play buffer is 4096 bytes then it is as follows:
	// [L 2048 bytes][R 2048 bytes] ... 
	// 
	// For mono the data is not interleaved, natch, as there is nothing but the single channel of program.
	//
	// The data buffer must be a multiple of the interleave rate, if the data is shorter fill the buffer
	// with silence prior to writing the data into it. For ADPCM silence is 0x80 for other formats it is
	// 0x00.
	// ------------------------------------------------------------------------------------------------------

	MyDebugf("Interleaving stereo audio data...");
	
	do	{
		gTarget	=	(char *)((KTU32)gMainBuffer+writePosition);
		gSource	=	(char *)((KTU32)gLeftBuffer+readPosition);
	
		memcpy(gTarget,gSource,interleaveRate);
	
		writePosition	+=	interleaveRate;
	
		gTarget	=	(char *)((KTU32)gMainBuffer+writePosition);
		gSource	=	(char *)((KTU32)gRightBuffer+readPosition);
	
		memcpy(gTarget,gSource,interleaveRate);
	
		writePosition	+=	interleaveRate;
		readPosition	+=	interleaveRate;
	
		}while(writePosition < gDataSize);

	// ------------------------------------------------------------------------------------------------------
	// free the temp buffers
	// ------------------------------------------------------------------------------------------------------
	
	syFree(gLeftBuffer);
	syFree(gRightBuffer);

	// ------------------------------------------------------------------------------------------------------
	// allocate a play buffer in sound memory (note the buffer required is (channels * PLAY_BUFFER_SIZE)
	// ------------------------------------------------------------------------------------------------------

	if(amHeapAlloc(&gPlayBuffer,STREAM_TEST_PLAY_BUFFER_SIZE * 2,32,AM_PURGABLE_MEMORY,KTNULL)==KTFALSE)
		{
		MyDebugf("amHeapAlloc(0x%x,%u,%u,AM_PURGABLE_MEMORY,KTNULL) FAIL\n",gPlayBuffer,STREAM_TEST_PLAY_BUFFER_SIZE * 2,32);
		return KTFALSE;
		}
	else
		MyDebugf("amHeapAlloc(0x%x,%u,%u,AM_PURGABLE_MEMORY,KTNULL)\n",gPlayBuffer,STREAM_TEST_PLAY_BUFFER_SIZE * 2,32);

	// ------------------------------------------------------------------------------------------------------
	// allocate a play buffer in sound memory
	// ------------------------------------------------------------------------------------------------------

gStream	=	 MyStreamPrepareBuffer((volatile KTU32 *)gMainBuffer,	
										gMainBufferSize,
										gPlayBuffer,
										gStreamVolume,
										64,
										44100,
										16,
										AM_STEREO,
										KTNULL);

#endif



return KTTRUE;
}
