// CAudio.cpp

#include <BeHeaders>

#pragma optimization_level 4

#define VOLUME_BITS	4

#include "CAudio.h"

CAudio::CAudio(
	int32	inVoices)
{
	mNumVoices = inVoices;
	mVoices = (SVoiceRec *)::calloc(sizeof(SVoiceRec), inVoices);
	MakeVolumeTable();

	mDACStream = new BDACStream();
	mSubscriber = new BSubscriber("DACMAN");
	mDACStream->SamplingRate(&mSampleRate);
	mMasterVol = 1.0;
	
	mID = spawn_thread(Entry, "DACMAN", B_REAL_TIME_PRIORITY, this);
	resume_thread(mID);
}

CAudio::~CAudio()
{
	mSubscriber->ExitStream(true);

	delete mSubscriber;
	delete mDACStream;
}

int32
CAudio::Entry(
	void	*arg)
{
	CAudio	*obj = (CAudio *)arg;
	
	return (obj->MainLoop());
}

int32
CAudio::MainLoop()
{
	mSubscriber->Subscribe(mDACStream);
	mSubscriber->EnterStream(NULL, TRUE, this, FillBuffer, NULL, FALSE);

	return 0;
}

bool
CAudio::FillBuffer(
	void 	*arg,
	char 	*inBuf,
	size_t	inCount,
	void 	*inHeader)
{
#pragma unused (inHeader)
	CAudio	*obj = (CAudio *)arg;
	
	return (obj->FillBuf(inBuf, inCount));
}

bool
CAudio::FillBuf(
	char 	*inBuf,
	size_t	inCount)
{
	int32		voices = mNumVoices;
	SVoicePtr		voiceP = mVoices;
	
	for (int32 i = 0; i<mNumVoices; i++, voiceP++) {
		int16	realVolume = voiceP->volume*mMasterVol;
		
		if (voiceP->active && realVolume) {
			size_t	count = inCount/2;
			int16	*outBuf = (int16 *)inBuf;
			int16	*pVolume = mVolLookup + ((realVolume >> (8 - VOLUME_BITS)) << 8);
				
			while (count) {
				*outBuf++ += pVolume[*(voiceP->cur)];
				voiceP->byteCount--;
				count--;
				
				if (voiceP->byteCount <= 0) {
					voiceP->byteCount += ((mSampleRate / voiceP->freq) * 2);
					voiceP->cur++;
					if (voiceP->cur >= voiceP->sample + voiceP->length) {
						if (!voiceP->loop) {
							voiceP->active = false;
							count = 0;
						}
						voiceP->cur = voiceP->sample;
					}
				}
			}
		}
	}

	return true;
}

void
CAudio::SetMasterVolume(
	int16		inNewVolume)
{
	mMasterVol = (float)inNewVolume/100.0;
}

void
CAudio::PlaySample(
	int32	inVoice,
	void		*inData,
	int32	inLen,
	int32	inFreq,
	int32	inVolume,
	int32	inLoop)
{
	SVoicePtr		voiceP = &mVoices[inVoice];

	voiceP->length = inLen;
	voiceP->sample = (char *)inData;
	voiceP->cur = (char *)inData;
	voiceP->freq = inFreq;
	voiceP->volume = inVolume;
	voiceP->loop = inLoop;
	voiceP->active = true;
	voiceP->byteCount = ((mSampleRate / mVoices[inVoice].freq) * 2);
}

void
CAudio::PlayStreamedSample(
	int32	inVoice,
	void		*inData,
	int32	inLen,
	int32	inFreq,
	int32	inVolume)
{
	SVoicePtr		voiceP = &mVoices[inVoice];
	uint32		leftToPlay = (voiceP->sample + voiceP->length) - voiceP->cur;
	uint32		totalLen = inLen + leftToPlay;
	
	if (totalLen > BUF_LEN)	 {	// this would make things sound bad
		totalLen = BUF_LEN;
		printf("c");
	}
		
	::memcpy(voiceP->streamBuf, voiceP->cur, leftToPlay);
	::memcpy(&voiceP->streamBuf[leftToPlay], inData, totalLen-leftToPlay);
	
	voiceP->length = totalLen;
	voiceP->sample = (char *)voiceP->streamBuf;
	voiceP->cur = (char *)voiceP->streamBuf;
	voiceP->freq = inFreq;
	voiceP->volume = inVolume;
	voiceP->loop = false;
	voiceP->active = true;
	voiceP->byteCount = ((mSampleRate / voiceP->freq) * 2);
}

void
CAudio::AdjustSample(
	int32	inVoice,
	int32	inFreq,
	int32	inVolume)
{
	mVoices[inVoice].freq = inFreq;
	mVoices[inVoice].volume = inVolume;
}

void
CAudio::StopSample(
	int32	inVoice)
{
	mVoices[inVoice].active = false;
}

void
CAudio::RestartSample(
	int32	inVoice)
{
	mVoices[inVoice].active = true;
	mVoices[inVoice].cur = mVoices[inVoice].sample;
}

void
CAudio::MakeVolumeTable()
{
	int32	i, j, v;

	mVolLookup = (int16 *)::calloc((1 << VOLUME_BITS) * 256 * sizeof (int16), 1);
	
	// do it
	for (j = 0; j < (1 << VOLUME_BITS); j++)
	{
		v = 0xff * j / ((1 << VOLUME_BITS) - 1);
		for (i = -128; i < 128; i++)
			mVolLookup[(j << 8) + (unsigned char)i] = (v * i) / 16;
	}
}
