/*
 * $Id: user.c 4.20 1998/11/01 10:20:47 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"

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

#define AHIsubBase P4AHIBase

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

	/* These two routines are called through assembly language
	 * stub code which is responsible for saving and restoring
	 * the contents of scratch registers.
	 */

VOID ASM
_AHIsub_Disable(
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	Forbid();
}

VOID ASM
_AHIsub_Enable(
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	Permit();
}

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

ULONG ASM
LIB_AHIsub_AllocAudioA(
	REG(a1) struct TagItem *			tagList,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ULONG result = AHISF_ERROR;

	ENTER();

	ObtainSemaphore(&P4AHIBase->ab_DriverSemaphore);

	if(NOT P4AHIBase->ab_DriverInUse)
	{
		struct AudioContext *ac;

		ac = CTO_ObtainAudioContext(0);
		if(ac != NULL)
		{
			P4AHIBase->ab_AudioContext		= ac;
			P4AHIBase->ab_DriverInUse		= TRUE;
			P4AHIBase->ab_RecordingSource	= RECORDSRC_Microphone;
			P4AHIBase->ab_Bits				= GetTagData(AHIDB_Bits,8,tagList);

			AudioCtrl->ahiac_DriverData = P4AHIBase;

			/* The following line is no longer necessary with
			 * concierto.library V6.1 and above, which emulates
			 * recording rates in software which are not
			 * supported in hardware.
			 */
			if(ConciertoBase->lib_Version < 6)
				AudioCtrl->ahiac_MixFreq = CTO_FindNearestSampleRate(AudioCtrl->ahiac_MixFreq);

			result = AHISF_KNOWSTEREO | AHISF_CANRECORD | AHISF_MIXING | AHISF_TIMING;
		}
	}

	ReleaseSemaphore(&P4AHIBase->ab_DriverSemaphore);

	RETURN(result);
	return(result);
}

VOID ASM
LIB_AHIsub_FreeAudio(
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	ObtainSemaphore(&P4AHIBase->ab_DriverSemaphore);

	if(P4AHIBase->ab_DriverInUse)
	{
		P4AHIBase->ab_DriverInUse = FALSE;

		CTO_ReleaseAudioContext(P4AHIBase->ab_AudioContext);
	}

	ReleaseSemaphore(&P4AHIBase->ab_DriverSemaphore);

	LEAVE();
}

ULONG ASM
LIB_AHIsub_Start(
	REG(d0) ULONG						Flags,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ULONG result = AHIE_NOMEM;

	ENTER();

	ObtainSemaphore(&P4AHIBase->ab_DriverSemaphore);

		/* Stop any play/record job in progress. */

	AHIsub_Stop(AHISF_PLAY | AHISF_RECORD,AudioCtrl);

	if(BIT_IS_SET(Flags,AHISB_PLAY) || BIT_IS_SET(Flags,AHISB_RECORD))
	{
		struct Task *task;
		ULONG *stack;
		ULONG bufferSize;

		if(BIT_IS_SET(Flags,AHISB_PLAY))
			bufferSize = 2 * AudioCtrl->ahiac_BuffSize;
		else
			bufferSize = RECORDBUFFER_SIZE;

		P4AHIBase->ab_WorkBuffer[0] = (UBYTE *)AllocVec(bufferSize * 2,MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR);
		if(P4AHIBase->ab_WorkBuffer[0] != NULL)
		{
			P4AHIBase->ab_WorkBuffer[1] = P4AHIBase->ab_WorkBuffer[0] + bufferSize;
			P4AHIBase->ab_WorkBufferSize = bufferSize;

			task	= &P4AHIBase->ab_Task;
			stack	= (ULONG *)(((ULONG)P4AHIBase->ab_TaskStack + 15) & ~15);

			task->tc_SPLower		= stack;
			task->tc_SPUpper		= (APTR)((ULONG)task->tc_SPLower + TASK_STACK_SIZE);
			task->tc_SPReg			= task->tc_SPUpper;
			task->tc_Node.ln_Type	= NT_TASK;
			task->tc_Node.ln_Name	= P4AHIBase->ab_Library.lib_Node.ln_Name;
			task->tc_UserData		= P4AHIBase;
			NewList(&task->tc_MemEntry);

			P4AHIBase->ab_AudioFlags	= Flags;
			P4AHIBase->ab_AudioCtrl		= AudioCtrl;

			P4AHIBase->ab_Parent = FindTask(NULL);

			/* Let's be smart and avoid starvation; our parent
			 * task must not be able to allocate more CPU cycles
			 * for itself than we will be able to get.
			 */
			task->tc_Node.ln_Pri = max(99,min(127,(int)P4AHIBase->ab_Parent->tc_Node.ln_Pri + 10));
			if(P4AHIBase->ab_TaskPriority != TASK_PRIORITY_UNSET)
				task->tc_Node.ln_Pri = P4AHIBase->ab_TaskPriority;

			Forbid();

			if(AddTask(task,AudioTaskEntry,NULL) != NULL)
			{
				SetSignal(0,SIGF_Sync);
				Wait(SIGF_Sync);

				SHOWMSG("handler task now running");

				result = AHIE_OK;
			}
			else
			{
				FreeVec(P4AHIBase->ab_WorkBuffer[0]);
				P4AHIBase->ab_WorkBuffer[0] = NULL;
			}

			Permit();
		}
	}

	ReleaseSemaphore(&P4AHIBase->ab_DriverSemaphore);

	RETURN(result);
	return(result);
}

VOID ASM
LIB_AHIsub_Stop(
	REG(d0) ULONG						Flags,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	ObtainSemaphore(&P4AHIBase->ab_DriverSemaphore);

	if(BIT_IS_SET(Flags,AHISB_PLAY) || BIT_IS_SET(Flags,AHISB_RECORD))
	{
		if(P4AHIBase->ab_TaskRunning)
		{
			SHOWMSG("handler task is running");

			P4AHIBase->ab_Parent = FindTask(NULL);

			SetSignal(0,SIGF_Sync);
			Signal(&P4AHIBase->ab_Task,SIGBREAKF_CTRL_C);

			Wait(SIGF_Sync);
		}

		SHOWMSG("stopped.");

		FreeVec(P4AHIBase->ab_WorkBuffer[0]);
		P4AHIBase->ab_WorkBuffer[0] = NULL;
	}

	ReleaseSemaphore(&P4AHIBase->ab_DriverSemaphore);

	LEAVE();
}

LONG ASM
LIB_AHIsub_GetAttr(
	REG(d0) ULONG						Attribute,
	REG(d1) LONG						Argument,
	REG(d2) LONG						Default,
	REG(a1) struct TagItem *			tagList,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	STATIC const STRPTR InputNames[4] =
	{
		"Microphone",
		"AV/MPEG",
		"Switcher",
		"Mixer"
	};

	BOOL numChannels,numBitsPerSample;
	LONG result;
	ULONG rate,index,counter;
	BOOL gotIt;

	ENTER();

	numChannels			= GetTagData(AHIDB_Stereo,FALSE,tagList) ? 2 : 1;
	numBitsPerSample	= GetTagData(AHIDB_Bits,8,tagList);

	switch(Attribute)
	{
		case AHIDB_Bits:

			SHOWMSG("AHIDB_Bits");

			result = numBitsPerSample;
			break;

		case AHIDB_Frequencies:

			SHOWMSG("AHIDB_Frequencies");

			index = 0;
			counter = 0;

			while((rate = CTO_NextSampleRate(&index)) != 0)
			{
				if(CTO_VerifyCompatibility(numBitsPerSample,numChannels,rate) != DSPERROR_None)
					break;

				counter++;
			}

			result = counter;

			break;

		case AHIDB_Frequency:

			SHOWMSG("AHIDB_Frequency");

			result = Default;

			index = 0;
			counter = 0;

			while((rate = CTO_NextSampleRate(&index)) != 0)
			{
				if(CTO_VerifyCompatibility(numBitsPerSample,numChannels,rate) != DSPERROR_None)
					break;

				if(counter == (ULONG)Argument)
				{
					result = (LONG)rate;
					break;
				}

				counter++;
			}

			break;

		case AHIDB_Index:

			SHOWMSG("AHIDB_Index");

			result = Default;

			gotIt = FALSE;

			/* concierto.library V6.1 and above will emulate
			 * recording/playback rates which the hardware
			 * does not support, which is why the following
			 * comparison tries to match given rates exactly.
			 * This won't work with older library versions.
			 */
			if(ConciertoBase->lib_Version >= 6)
			{
				index = 0;
				counter = 0;
	
				while((rate = CTO_NextSampleRate(&index)) != 0)
				{
					if(CTO_VerifyCompatibility(numBitsPerSample,numChannels,rate) != DSPERROR_None)
						break;
	
					if(rate == Argument)
					{
						result = (LONG)counter;
						gotIt = TRUE;
						break;
					}
	
					counter++;
				}
			}

			if(NOT gotIt)
			{
				ULONG closest = CTO_FindNearestSampleRate(Argument);

				index = 0;
				counter = 0;
	
				while((rate = CTO_NextSampleRate(&index)) != 0)
				{
					if(CTO_VerifyCompatibility(numBitsPerSample,numChannels,rate) != DSPERROR_None)
						break;
	
					if(rate == closest)
					{
						result = (LONG)counter;
						break;
					}
	
					counter++;
				}
			}

			break;

		case AHIDB_Author:

			SHOWMSG("AHIDB_Author");

			result = (LONG)"Olaf Barthel";
			break;

		case AHIDB_Copyright:

			SHOWMSG("AHIDB_Copyright");

			result = (LONG)"Copyright © 1997-1998 Village Tronic Computer";
			break;

		case AHIDB_Version:

			SHOWMSG("AHIDB_Version");

			result = (LONG)P4AHIBase->ab_Library.lib_IdString;
			break;

		case AHIDB_Record:

			SHOWMSG("AHIDB_Record");

			result = TRUE;
			break;

		case AHIDB_FullDuplex:

			SHOWMSG("AHIDB_FullDuplex");

			result = FALSE;
			break;

		case AHIDB_Realtime:

			SHOWMSG("AHIDB_Realtime");

			result = TRUE;
			break;

		case AHIDB_MaxRecordSamples:

			SHOWMSG("AHIDB_MaxRecordSamples");

			result = RECORDBUFFER_SIZE;
			break;

		case AHIDB_MinMonitorVolume:

			SHOWMSG("AHIDB_MinMonitorVolume");

			result = 0x00010000 / 16;
			break;

		case AHIDB_MaxMonitorVolume:
		case AHIDB_MinInputGain:
		case AHIDB_MaxInputGain:

			SHOWMSG("AHIDB_MaxMonitorVolume..AHIDB_MaxInputGain");

			result = 0x00010000;
			break;

		case AHIDB_Inputs:

			SHOWMSG("AHIDB_Inputs");

			result = 4;
			break;

		case AHIDB_Input:

			SHOWMSG("AHIDB_Input");

			result = (LONG)InputNames[Argument];
			break;

		case AHIDB_Outputs:

			SHOWMSG("AHIDB_Outputs");

			result = 1;
			break;

		case AHIDB_Output:

			SHOWMSG("AHIDB_Output");

			result = (LONG)"Line";
			break;

		case AHIDB_MinOutputVolume:

			SHOWMSG("AHIDB_MinOutputVolume");

			result = 0;
			break;

		case AHIDB_MaxOutputVolume:

			SHOWMSG("AHIDB_MaxOutputVolume");

			result = 0x00010000;
			break;

		default:

			SHOWVALUE(Attribute);

			result = Default;
			break;
	}

	RETURN(result);
	return(result);
}

LONG ASM
LIB_AHIsub_HardwareControl(
	REG(d0) ULONG						Attribute,
	REG(d1) LONG						Argument,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	LONG result = FALSE;

	ENTER();

	SHOWVALUE(Attribute);

	switch(Attribute)
	{
		case AHIC_Input_Query:

			SHOWMSG("AHIC_Input_Query");

			result = P4AHIBase->ab_RecordingSource;
			break;

		case AHIC_Input:

			SHOWMSG("AHIC_Input");
			SHOWVALUE(Argument);

			P4AHIBase->ab_RecordingSource = Argument;

			result = TRUE;
			break;

		case AHIC_MonitorVolume_Query:

			SHOWMSG("AHIC_MonitorVolume_Query");

			result = P4AHIBase->ab_MonitorVolume;
			break;

		case AHIC_MonitorVolume:

			SHOWMSG("AHIC_MonitorVolume");
			SHOWVALUE(Argument);

			P4AHIBase->ab_MonitorVolume = Argument;

			result = TRUE;
			break;
	}

	RETURN(result);
	return(result);
}

/*****************************************************************************
 *
 *	All routines below are unimplemented.
 *
 ****************************************************************************/

VOID ASM
LIB_AHIsub_Update(
	REG(d0) ULONG						Flags,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	SHOWVALUE(Flags);

	LEAVE();
}

ULONG ASM
LIB_AHIsub_SetVol(
	REG(d0) UWORD						Channel,
	REG(d1) Fixed						Volume,
	REG(d2) sposition					Pan,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(d3) ULONG						Flags,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	SHOWVALUE(Channel);
	SHOWVALUE(Volume);
	SHOWVALUE(Pan);
	SHOWVALUE(Flags);

	RETURN(AHIS_UNKNOWN);
	return(AHIS_UNKNOWN);
}

ULONG ASM
LIB_AHIsub_SetFreq(
	REG(d0) UWORD						Channel,
	REG(d1) ULONG						Freq,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(d2) ULONG						Flags,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	SHOWVALUE(Channel);
	SHOWVALUE(Freq);
	SHOWVALUE(Flags);

	RETURN(AHIS_UNKNOWN);
	return(AHIS_UNKNOWN);
}

ULONG ASM
LIB_AHIsub_SetSound(
	REG(d0) UWORD						Channel,
	REG(d1) UWORD						Sound,
	REG(d2) ULONG						Offset,
	REG(d3) LONG						Length,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(d4) ULONG						Flags,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	SHOWVALUE(Channel);
	SHOWVALUE(Sound);
	SHOWVALUE(Offset);
	SHOWVALUE(Length);
	SHOWVALUE(Flags);

	RETURN(AHIS_UNKNOWN);
	return(AHIS_UNKNOWN);
}

ULONG ASM
LIB_AHIsub_SetEffect(
	REG(a0) APTR						Effect,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	SHOWVALUE(Effect);

	RETURN(AHIS_UNKNOWN);
	return(AHIS_UNKNOWN);
}

ULONG ASM
LIB_AHIsub_LoadSound(
	REG(d0) UWORD						Sound,
	REG(d1) ULONG						Type,
	REG(a0) APTR						Info,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	SHOWVALUE(Sound);
	SHOWVALUE(Type);
	SHOWVALUE(Info);

	RETURN(AHIS_UNKNOWN);
	return(AHIS_UNKNOWN);
}

ULONG ASM
LIB_AHIsub_UnloadSound(
	REG(d0) UWORD						Sound,
	REG(a2) struct AHIAudioCtrlDrv *	AudioCtrl,
	REG(a6) struct P4AHIBase *			P4AHIBase)
{
	ENTER();

	SHOWVALUE(Sound);

	RETURN(AHIS_UNKNOWN);
	return(AHIS_UNKNOWN);
}
