/* Made Dec 1998 by Claes Löfqvist */

#include "BeosSound.h"
#include "Converter.cc"

#include <stdio.h>

#define MAIN_FREQUENCE 44100.0

class Sample
{
public:
	Sample(signed char *data, int size, int freq, int volume, bool repeat);
	Sample(signed short *data, int size, int freq, int volume, bool repeat);
	~Sample();
	void Read(short *buff, int len);
	void Stop()      { Active= false; }
	bool IsStopped() { return (!Active); }
	void Restart()   { Active= true; Pos=0; }
//	void Adjust(int freq, int volume) { Active= true; Freq= freq; Volume= volume; Step= Freq/MAIN_FREQUENCE; }
	void AdjustFrequency(int freq)   { Active= true; Freq= freq; Step= Freq/MAIN_FREQUENCE; }
	void AdjustVolume   (int volume) { Active= true; Volume= volume; }
private:
	void Init(int size, int freq, int volume, bool repeat);
	bool Active;
	signed short *Data;
	float Size;
	float Step;
	float Freq;
	float Pos;
	bool Repeat;
	long Volume;
}; 

#define KiloBytes(x) ((x)*1024)
#define MegaBytes(x) ((x)*1024*1024)

#define VOLUME_BASE (1.0/32768.0)
#define AUDIO_BUFFER_SIZE 10000

#define NO_CHANNEL  -1
static int NrOfStreams= NO_CHANNEL;
static int NrOfSamples= NO_CHANNEL;

static Sample *Samples[MAX_SAMPLES];
static sem_id SampleSem;	

#define INIT_VOLUME 255
static int MasterVolume;
static float ActualVolume;

static void stream_func(void *theCookie, void *buffer, size_t size, const media_raw_audio_format &format) { 
	static char streambuf[KiloBytes(8)];	// Temporary buffer for stream
	register size_t i; 
	float *buf = (float *) buffer; 
	size_t float_size = size/4;
	short *b=(short *)streambuf;

	if (format.format == media_raw_audio_format::B_AUDIO_FLOAT && theCookie) {
		if (NrOfStreams>0) {
			if (((Converter *)theCookie)->Read(streambuf, float_size*sizeof(short))) {
				for (i=float_size; i>0; --i) {
					*buf++= (float)(*b++)*ActualVolume;
				}
			}
		}
		else if (NrOfSamples>0) {
			float_size/= 2; // Want the MONO length!
			memset(b,0,size);
			if (acquire_sem(SampleSem)==B_NO_ERROR) {
				for (i=0; (int)i<NrOfSamples; ++i) {
					if (Samples[i])
						Samples[i]->Read(b,float_size);
				}
				release_sem(SampleSem);
			}
			for (i=float_size; i>0; --i) {
				*buf++= (float)(*b++)*ActualVolume;
				*buf= *(buf-1);
				buf++;
			}
		}
		else
			memset(buffer,0,size);
	}
}

void Sample::Init(int size, int freq, int volume, bool repeat)
{
	Data= new short [size];
	if (Data) {
		Active= true;
		Size= size;
		Pos= 0;
		Freq= freq;
		Step= Freq/MAIN_FREQUENCE;
		Volume= volume;
		Repeat= repeat;
	}
}

Sample::Sample(signed char *data, int size, int freq, int volume, bool repeat)
{
	short *DataPtr;
	
	Init(size, freq, volume, repeat);
	if ((DataPtr=Data)!=NULL) {
		for (; size>0; --size)
			*DataPtr++= (short)((long)(*data++) * (long)256);
	}
}

Sample::Sample(signed short *data, int size, int freq, int volume, bool repeat)
{
	short *DataPtr;
	
	Init(size, freq, volume, repeat);
	if ((DataPtr=Data)!=NULL) {
		for (; size>0; --size)
			*DataPtr++= *data++;
	}
}

Sample::~Sample()
{
	if (Data) delete Data;
}

void Sample::Read (short *buff, int len)
{
	if (Active && Volume && Data) {
		long Vol= Volume/NrOfSamples;
		//long Vol= (NrOfSamples==1?255:160);
		for (; len>0; --len) {
			*(buff++)+= (short)((long)Data[(int)Pos]*Vol/255L);
			Pos+= Step;
			while (Pos>Size) { 
				Pos-= Size;
				if (!Repeat) { Active= false; return; }
			}
		}
	}
}

void BeosMameSound::On(int NoAdjust, int NoGaps)
{
	media_raw_audio_format audio_format;

	audio_format.frame_rate= 44100;
	audio_format.channel_count= 2;
	audio_format.format= media_raw_audio_format::B_AUDIO_FLOAT;
#ifdef __POWERPC__
	audio_format.byte_order= B_MEDIA_BIG_ENDIAN;
#else
	audio_format.byte_order= B_MEDIA_LITTLE_ENDIAN;
#endif
	audio_format.buffer_size= 4096;

	if (!SoundOn) {
		SoundOn= true;	
		converter= new Converter(AUDIO_BUFFER_SIZE);
		musicPlayer= new BSoundPlayer(&audio_format,"BeMame", stream_func, NULL, (void *)converter);
		if (converter && musicPlayer) {
			converter->SetSpeedAdjustment( (NoAdjust==0 || NoAdjust==-1)?true:false );
			converter->SetNoGaps( NoGaps );
			musicPlayer->Start();
			musicPlayer->SetHasData(true);
		}
		else
			Off();
	}
}
 
BeosMameSound::BeosMameSound()
{
	int L;
	
	converter= NULL;
	musicPlayer= NULL;
	SoundOn= false;
	for (L=0; L<MAX_SAMPLES; ++L)
		Samples[L]= NULL;
	SampleSem= create_sem(1, "Sample");
	SetMasterVolume (INIT_VOLUME);
}

void BeosMameSound::Off()
{
	int L;
	
	if (SoundOn) {
		if (musicPlayer) {
			musicPlayer->SetHasData(false);
		}
		 
		if (converter) converter->Discard();
 
		if (musicPlayer) {
			musicPlayer->Stop(true,true);
			delete musicPlayer;
			musicPlayer= NULL;
		}
	
		if (converter) delete converter;
		converter= NULL;
  
		SoundOn= false;
		
		for (L=0; L<MAX_SAMPLES; ++L) {
			if (Samples[L]) delete Samples[L];
			Samples[L]= NULL;
		}
		NrOfStreams= NO_CHANNEL;
		NrOfSamples= NO_CHANNEL;
		SetMasterVolume (INIT_VOLUME);
	}
} 

BeosMameSound::~BeosMameSound()
{
	Off();
	delete_sem(SampleSem);
}

#define SOUND_BUFF_LEN 10000
static char SoundBuffer[SOUND_BUFF_LEN];
 
void BeosMameSound::Play8BitsStream  (int channel,signed char *data,int len,int freq,int volume)
{
	register short *buff= (short *)SoundBuffer;
	register short Inc= 256/NrOfStreams;
	
	if (channel>=NrOfStreams) NrOfStreams= channel+1/*, printf("Sound channel %d activated\n", channel)*/;
	if (channel==0) {
		converter->SetInStream(freq,false);
		converter->Write(SoundBuffer, len*sizeof(short), false);
		for (; len>0; --len)
			*(buff++)= *(data++)*Inc;
	}
	else {
		for (; len>0; --len)
			*(buff++)+= *(data++)*Inc;
	}
}
void BeosMameSound::Play16BitsStream  (int channel,signed short *data,int len,int freq,int volume)
{
	register short *buff= (short *)SoundBuffer;
	
	if (channel>=NrOfStreams) NrOfStreams= channel+1;
	if (channel==0) {
		converter->SetInStream(freq,false);
		converter->Write(SoundBuffer, len, false);
		len/= sizeof(short);
		for (; len>0; --len)
			*(buff++)= *(data++)/NrOfStreams;
	}
	else {
		len/= sizeof(short);
		for (; len>0; --len)
			*(buff++)+= *(data++)/NrOfStreams;
	}
}

void BeosMameSound::Play8BitsSample  (int channel,signed char *data,int len,int freq,int volume,int loop)
{
	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (channel>=NrOfSamples) NrOfSamples= channel+1;
		if (Samples[channel]) delete Samples[channel];
		Samples[channel]= new Sample(data, len, freq, volume, loop);
		release_sem(SampleSem);
	}
}

void BeosMameSound::Play16BitsSample  (int channel,signed short *data,int len,int freq,int volume,int loop)
{
	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (channel>=NrOfSamples) NrOfSamples= channel+1;
		if (Samples[channel]) delete Samples[channel];
		Samples[channel]= new Sample(data, len/sizeof(short), freq, volume, loop);
		release_sem(SampleSem);
	}
}
/* This was used before MAME 0.35...
void BeosMameSound::AdjustSample (int channel,int freq,int volume)
{
	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (Samples[channel])
			Samples[channel]->Adjust(freq, volume);
		release_sem(SampleSem);
	}
}
*/
void BeosMameSound::AdjustSampleFrequency (int channel,int freq)
{
	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (Samples[channel])
			Samples[channel]->AdjustFrequency(freq);
		release_sem(SampleSem);
	}
}

void BeosMameSound::AdjustSampleVolume (int channel,int volume)
{
	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (Samples[channel])
			Samples[channel]->AdjustVolume(volume);
		release_sem(SampleSem);
	}
}

void BeosMameSound::StopSample (int channel)
{
	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (Samples[channel])
			Samples[channel]->Stop();
		release_sem(SampleSem);
	}
}

void BeosMameSound::RestartSample (int channel)
{
	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (Samples[channel])
			Samples[channel]->Restart();
		release_sem(SampleSem);
	}
}

bool BeosMameSound::GetSampleStatus  (int channel)
{
	bool Stopped= false;

	if (channel<MAX_SAMPLES && acquire_sem(SampleSem)==B_NO_ERROR) {
		if (Samples[channel])
			Stopped= Samples[channel]->IsStopped();
		release_sem(SampleSem);
	}
	return (Stopped);
}

void BeosMameSound::SetMasterVolume  (int volume)
{
	if (volume>255) volume= 255;
	if (volume<  0) volume=   0;
	MasterVolume= volume;
	float Factor= (float)MasterVolume/255.0;
	ActualVolume= VOLUME_BASE*Factor*Factor;
}

int BeosMameSound::GetMasterVolume  ()
{
	return MasterVolume;
}


