/*
 * $Id: task.c 4.20 1998/04/08 18:52:45 olsen Exp olsen $
 *
 * :ts=4
 *
 * Copyright © 1997-1998 Village Tronic Computer
 */

#ifndef _GLOBAL_H
#include "global.h"
#endif	/* _GLOBAL_H */

/*#define DEBUG*/
#include "Assert.h"

/****************************************************************************/

STATIC VOID
TaskRecord(struct P4AHIBase *P4AHIBase)
{
	struct AHIAudioCtrlDrv *AudioCtrl = P4AHIBase->ab_AudioCtrl;
	struct AudioContext *ac = P4AHIBase->ab_AudioContext;
	UBYTE *buffer = P4AHIBase->ab_WorkBuffer[0];
	LONG numChannels,interleave;
	APTR sampleLeft,sampleRight;
	ULONG rightPlus;
	ULONG sampleBytes,bitsPerSample = P4AHIBase->ab_Bits;
	ULONG signals;
	LONG i;
	struct AHIRecordMessage recordMessage;

	ENTER();

	if(AudioCtrl->ahiac_BuffType == AHIST_S16S)
	{
		numChannels = 2;
		rightPlus	= 2;
	}
	else
	{
		numChannels = 1;
		rightPlus	= 0;
	}

	interleave = 4;

	sampleLeft	= buffer;
	sampleRight	= buffer + rightPlus;

	sampleBytes = RECORDBUFFER_SIZE / (sizeof(WORD) * 2);

	P4AHIBase->ab_Query.dqp_LeftChannel = P4AHIBase->ab_WorkBuffer[1];
	P4AHIBase->ab_Query.dqp_RightChannel= P4AHIBase->ab_WorkBuffer[1] + rightPlus;
	P4AHIBase->ab_Query.dqp_NumBytesToGo= sampleBytes * (bitsPerSample / 8);

	recordMessage.ahirm_Type	= AHIST_S16S;
	recordMessage.ahirm_Buffer	= buffer;
	recordMessage.ahirm_Length	= sampleBytes;

	SHOWVALUE(recordMessage.ahirm_Length);

	CTO_StartRecording(ac,
		DSPA_SampleDataLeft,	sampleLeft,
		DSPA_SampleDataRight,	sampleRight,
		DSPA_NumSampleBytes,	sampleBytes * (bitsPerSample / 8),
		DSPA_RecordingRate,		AudioCtrl->ahiac_MixFreq,
		DSPA_RecordingSource,	P4AHIBase->ab_RecordingSource,
		DSPA_RecordMonitorVolume,(P4AHIBase->ab_RecordingSource != RECORDSRC_Microphone) ? CTO_VOLUME_Unity : 0,
		DSPA_NumChannels,		numChannels,
		DSPA_NumBitsPerSample,	bitsPerSample,
		DSPA_SampleInterleave,	interleave,
		DSPA_ReloadHook,		&P4AHIBase->ab_ReloadHook,
		DSPA_WakeTask,			FindTask(NULL),
		DSPA_LeftInputVolume,	P4AHIBase->ab_MonitorVolume,
		DSPA_RightInputVolume,	P4AHIBase->ab_MonitorVolume,
	TAG_DONE);

	if(bitsPerSample == 8)
	{
		if(numChannels == 2)
		{
			while(TRUE)
			{
				signals = Wait(SIGF_Reload | SIGF_Kill);

				if(BIT_IS_SET(signals,SIGB_Kill))
				{
					break;
				}

				if(BIT_IS_SET(signals,SIGB_Reload))
				{
					// L_R_L_R_L_R_L_R_...

					for(i = 0 ; i < sampleBytes ; i += 2)
						buffer[i+1] = buffer[i];

					// LLRRLLRRLLRRLLRR...

					CallHookPkt(AudioCtrl->ahiac_SamplerFunc,AudioCtrl,&recordMessage);

					recordMessage.ahirm_Buffer = P4AHIBase->ab_Query.dqp_LeftChannel;

					P4AHIBase->ab_Query.dqp_LeftChannel = buffer;
					P4AHIBase->ab_Query.dqp_RightChannel = buffer + rightPlus;
				}
			}
		}
		else
		{
			while(TRUE)
			{
				signals = Wait(SIGF_Reload | SIGF_Kill);

				if(BIT_IS_SET(signals,SIGB_Kill))
				{
					break;
				}

				if(BIT_IS_SET(signals,SIGB_Reload))
				{
					// L___L___L___L___...

					for(i = 0 ; i < sampleBytes ; i += 4)
					{
						buffer[i+1] = buffer[i];
						buffer[i+2] = buffer[i];
						buffer[i+3] = buffer[i];
					}

					// LLLLLLLLLLLLLLLL...

					CallHookPkt(AudioCtrl->ahiac_SamplerFunc,AudioCtrl,&recordMessage);

					recordMessage.ahirm_Buffer = P4AHIBase->ab_Query.dqp_LeftChannel;

					P4AHIBase->ab_Query.dqp_LeftChannel = buffer;
					P4AHIBase->ab_Query.dqp_RightChannel = buffer + rightPlus;
				}
			}
		}
	}
	else
	{
		if(numChannels == 2)
		{
			while(TRUE)
			{
				signals = Wait(SIGF_Reload | SIGF_Kill);

				if(BIT_IS_SET(signals,SIGB_Kill))
				{
					break;
				}

				if(BIT_IS_SET(signals,SIGB_Reload))
				{
					// LLRRLLRRLLRRLLRR...

					CallHookPkt(AudioCtrl->ahiac_SamplerFunc,AudioCtrl,&recordMessage);

					recordMessage.ahirm_Buffer = P4AHIBase->ab_Query.dqp_LeftChannel;

					P4AHIBase->ab_Query.dqp_LeftChannel = buffer;
					P4AHIBase->ab_Query.dqp_RightChannel = buffer + rightPlus;
				}
			}
		}
		else
		{
			while(TRUE)
			{
				signals = Wait(SIGF_Reload | SIGF_Kill);

				if(BIT_IS_SET(signals,SIGB_Kill))
				{
					break;
				}

				if(BIT_IS_SET(signals,SIGB_Reload))
				{
					// LL__LL__LL__LL__...

					for(i = 0 ; i < sampleBytes ; i += 4)
					{
						buffer[i+2] = buffer[i];
						buffer[i+3] = buffer[i+1];
					}

					// LLLLLLLLLLLLLLLL...

					CallHookPkt(AudioCtrl->ahiac_SamplerFunc,AudioCtrl,&recordMessage);

					recordMessage.ahirm_Buffer = P4AHIBase->ab_Query.dqp_LeftChannel;

					P4AHIBase->ab_Query.dqp_LeftChannel = buffer;
					P4AHIBase->ab_Query.dqp_RightChannel = buffer + rightPlus;
				}
			}
		}
	}

	CTO_Abort(ac);

	LEAVE();
}

/****************************************************************************/

LONG ASM
ReloadHookFunc(
	REG(a0) struct Hook *			hook,
	REG(a2) struct DSPQueryPacket *	pkt)
{
	struct P4AHIBase *P4AHIBase = (struct P4AHIBase *)hook->h_Data;

	pkt->dqp_LeftChannel	= P4AHIBase->ab_Query.dqp_LeftChannel;
	pkt->dqp_RightChannel	= P4AHIBase->ab_Query.dqp_RightChannel;

	Signal(&P4AHIBase->ab_Task,SIGF_Reload);

	return(P4AHIBase->ab_Query.dqp_NumBytesToGo);
}

/****************************************************************************/

typedef BOOL (* ASM PRETIMER)(REG(a2) struct AHIAudioCtrlDrv *);
typedef VOID (* ASM POSTTIMER)(REG(a2) struct AHIAudioCtrlDrv *);

/****************************************************************************/

STATIC VOID
TaskPlay(struct P4AHIBase *P4AHIBase)
{
	struct AHIAudioCtrlDrv *AudioCtrl = P4AHIBase->ab_AudioCtrl;
	struct AudioContext *ac = P4AHIBase->ab_AudioContext;
	UBYTE *buffer;
	LONG numChannels,interleave;
	APTR sampleLeft,sampleRight;
	ULONG bitsPerSample = P4AHIBase->ab_Bits;
	ULONG rightPlus,whichBuffer = 0;
	ULONG signals;
	struct DSPQueryPacket *pkt = &P4AHIBase->ab_Query;
	BOOL firstRun = TRUE;
	LONG framesAvailable,framesInBuffer,numFrameBytes;
	LONG i;
	BOOL stopRightThere;
	PRETIMER PreTimer = (PRETIMER)AudioCtrl->ahiac_PreTimer;
	POSTTIMER PostTimer = (POSTTIMER)AudioCtrl->ahiac_PostTimer;

	ENTER();

	SHOWVALUE(PreTimer);
	SHOWVALUE(PostTimer);

	if(AudioCtrl->ahiac_BuffType == AHIST_S16S)
	{
		numChannels = 2;
		rightPlus	= 2;
		interleave	= 4;
	}
	else
	{
		numChannels = 1;
		rightPlus	= 0;
		interleave	= 2;
	}

	numFrameBytes = AudioCtrl->ahiac_BuffSamples * (bitsPerSample / 8);
	framesAvailable = P4AHIBase->ab_WorkBufferSize / (sizeof(WORD) * AudioCtrl->ahiac_BuffSamples * numChannels);

	SHOWVALUE(P4AHIBase->ab_WorkBufferSize);
	SHOWVALUE(AudioCtrl->ahiac_PlayerFunc);
	SHOWVALUE(AudioCtrl->ahiac_MixerFunc);
	SHOWVALUE(framesAvailable);
	SHOWVALUE(numFrameBytes);

	pkt->dqp_LeftChannel	= NULL;
	pkt->dqp_RightChannel	= NULL;

	do
	{
		buffer = P4AHIBase->ab_WorkBuffer[whichBuffer];

		framesInBuffer = 0;
		stopRightThere = FALSE;
		for(i = 0 ; i < framesAvailable ; i++)
		{
			CallHookPkt(AudioCtrl->ahiac_PlayerFunc,AudioCtrl,NULL);

			if(NO (*PreTimer)(AudioCtrl))
			{
				CallHookPkt(AudioCtrl->ahiac_MixerFunc,AudioCtrl,buffer);
				buffer += sizeof(WORD) * AudioCtrl->ahiac_BuffSamples * numChannels;
				framesInBuffer++;
			}
			else
			{
				stopRightThere = TRUE;
			}

			(*PostTimer)(AudioCtrl);

			signals = SetSignal(0,SIGF_Kill);
			if(signals & SIGF_Kill)
			{
				SHOWMSG("playback stopped.");
				goto AbortAndLeave;
			}

			if(stopRightThere)
				break;
		}

		buffer = P4AHIBase->ab_WorkBuffer[whichBuffer];
		whichBuffer ^= 1;

		sampleLeft	= buffer;
		sampleRight	= buffer + rightPlus;

		pkt->dqp_NumBytesToGo = numFrameBytes * framesInBuffer;

		if(firstRun)
		{
			firstRun = FALSE;

			SHOWMSG("Starting playback...");

			SHOWMSG("DSPA_SampleDataLeft");
			SHOWVALUE(sampleLeft);
			SHOWMSG("DSPA_SampleDataRight");
			SHOWVALUE(sampleRight);
			SHOWMSG("DSPA_NumSampleBytes");
			SHOWVALUE(pkt->dqp_NumBytesToGo);
			SHOWMSG("DSPA_PlaybackRate");
			SHOWVALUE(AudioCtrl->ahiac_MixFreq);
			SHOWMSG("DSPA_NumChannels");
			SHOWVALUE(numChannels);
			SHOWMSG("DSPA_NumBitsPerSample");
			SHOWVALUE(bitsPerSample);
			SHOWMSG("DSPA_SampleInterleave");
			SHOWVALUE(interleave);
			SHOWMSG("DSPA_ReloadHook");
			SHOWVALUE(&P4AHIBase->ab_ReloadHook);
			SHOWMSG("DSPA_WakeTask");
			SHOWVALUE(FindTask(NULL));

			CTO_StartPlayback(ac,
				DSPA_SampleDataLeft,	sampleLeft,
				DSPA_SampleDataRight,	sampleRight,
				DSPA_NumSampleBytes,	pkt->dqp_NumBytesToGo,
				DSPA_PlaybackRate,		AudioCtrl->ahiac_MixFreq,
				DSPA_NumChannels,		numChannels,
				DSPA_NumBitsPerSample,	bitsPerSample,
				DSPA_SampleInterleave,	interleave,
				DSPA_ReloadHook,		&P4AHIBase->ab_ReloadHook,
				DSPA_WakeTask,			FindTask(NULL),
			TAG_DONE);

			SHOWMSG("Ok.");

			signals = SetSignal(0,SIGF_Kill);
		}
		else
		{
			pkt->dqp_LeftChannel	= sampleLeft;
			pkt->dqp_RightChannel	= sampleRight;

			signals = Wait(SIGF_Kill | SIGF_Reload);
		}

		if(signals & SIGF_Kill)
		{
			SHOWMSG("playback stopped.");
			break;
		}
	}
	while(BIT_IS_UNSET(signals,SIGB_Kill));

AbortAndLeave:
	CTO_Abort(ac);

	LEAVE();
}

/****************************************************************************/

#undef SysBase

extern struct ExecBase *AbsExecBase;

/****************************************************************************/

VOID
AudioTaskEntry(VOID)
{
	struct ExecBase *SysBase = AbsExecBase;
	struct Task *thisTask;
	struct P4AHIBase *P4AHIBase;
	ULONG Flags;

	ENTER();

	Forbid();

	thisTask = FindTask(NULL);

	P4AHIBase = (struct P4AHIBase *)thisTask->tc_UserData;

	P4AHIBase->ab_TaskRunning = TRUE;
	Signal(P4AHIBase->ab_Parent,SIGF_Sync);

	Permit();

	Flags = P4AHIBase->ab_AudioFlags;

	if(BIT_IS_SET(Flags,AHISB_PLAY))
	{
		SHOWMSG("Calling TaskPlay");

		TaskPlay(P4AHIBase);
	}
	else if(BIT_IS_SET(Flags,AHISB_RECORD))
	{
		SHOWMSG("Calling TaskRecord");

		TaskRecord(P4AHIBase);
	}
	else
	{
		SHOWMSG("Did nothing.");
		SHOWVALUE(Flags);
	}

	SHOWMSG("Task work done.");

	Forbid();

	Signal(P4AHIBase->ab_Parent,SIGF_Sync);
	P4AHIBase->ab_TaskRunning = FALSE;

	LEAVE();
}
