seqpLoop.c 5.44 KB
#include <assert.h>
#include <libaudio.h>
#include "UniversalSP.h"
#include "audiotest.h"
#include "audio.h"
#include "seqpTest.h"
#include "seqpLoop.h"


extern ALHeap		gHeapRec;
extern ALBankFile *	gBankFile;
extern ALUSPlayer *	gSeqp;
extern ALUSeq *		gSeq;
extern u8 *		gSeqData;

static ALUSMarker	startMkr, endMkr;


static void	_StartLoopTest (ALUSPlayer *seqp, short testID);
static void	_GetLoopTestSequence (void);
static u32	_qnotes2ticks (ALUSeq *seq, u32 qnotes);
static s32	_DivS32ByF32 (s32 i, f32 f);



void
SeqpLoop_Init (void)
{
    ALSeqpConfig        seqc;

    PRINTF("Test loops and markers\n");

    /* Setup this test's retrace proc and data. */
    seqpTest.retraceProc = SeqpLoop_RetraceProc;
    seqpTest.buttonProc = SeqpLoop_ButtonProc;
    seqpTest.retraceData = (TRetraceData)gSeqp;

    /* Get the loop test sequence. */
    _GetLoopTestSequence ();

    /* Setup the loop test sequence player. */
    seqc.maxVoices      = 8;
    seqc.maxEvents      = MAX_SEQP_EVENTS;
    seqc.maxChannels    = 16;
    seqc.heap           = &gHeapRec;
    seqc.initOsc        = 0;
    seqc.updateOsc      = 0;
    seqc.stopOsc        = 0;
#ifdef _DEBUG
    seqc.debugFlags     = NO_VOICE_ERR_MASK |NOTE_OFF_ERR_MASK | NO_SOUND_ERR_MASK;
#endif

    alUSPNew (gSeqp, &seqc);
    alUSPSetSeq (gSeqp, gSeq);
    alUSPSetBank (gSeqp, gBankFile->bankArray[0]);
}



void
SeqpLoop_Fini (void)
{
    alUSPDelete (gSeqp);
}


void
SeqpLoop_ButtonProc(u16 oldButtons, u16 newButtons)
{
    u16 button;

    button = oldButtons^newButtons;  /* get the ones that have changed */
    button &= newButtons;            /* get only button downs */

    if (button & CONT_START)
	TestDone ();
}


void
SeqpLoop_RetraceProc (TRetraceData data)
{
    static short	sLoopTestNum = 0;
    static char		sCheckForEOS = TRUE;
    ALUSPlayer *	seqp = (ALUSPlayer *)data;

    if (sCheckForEOS && alUSPGetState(seqp) == AL_STOPPED)
    {
	sCheckForEOS = FALSE;
	_StartLoopTest (seqp, sLoopTestNum++);
    }

    if (!sCheckForEOS && alUSPGetState(seqp) == AL_PLAYING)
	sCheckForEOS = TRUE;
}


static void
_StartLoopTest (ALUSPlayer *seqp, short testID)
{
    ALUSeq *	seq = alUSPGetSeq (seqp);
    ALUSMarker	zeroMkr;
    u32		startTick;
    u32		endTick;
    s32		numLoops = 3;

    switch (testID)
    {
    case 0:
	startTick = 0;
	endTick = 0x7fffffff;
    break;

    case 1:
	startTick = 0;
	endTick = _qnotes2ticks(seq, 3) + 1;
    break;

    case 2:
	startTick = 0;
	endTick = _qnotes2ticks(seq, 2) + 1;
    break;

    case 3:
	startTick = 0;
	endTick = _qnotes2ticks(seq, 1) + 1;
    break;

    case 4:
	startTick = 0;
	endTick = 0;
    break;

    case 5:
	startTick = _qnotes2ticks(seq, 1) + 1;
	endTick = 0x7fffffff;
    break;

    case 6:
	startTick = _qnotes2ticks(seq, 2) + 1;
	endTick = 0x7fffffff;
    break;

    case 7:
	startTick = _qnotes2ticks(seq, 3) + 1;
	endTick = 0x7fffffff;
    break;

    case 8:
	startTick = _qnotes2ticks(seq, 1) + 1;
	endTick = _qnotes2ticks(seq, 3) + 1;
    break;

    case 9:
	startTick = _qnotes2ticks(seq, 3) + 1;
	endTick = _qnotes2ticks(seq, 1) + 1;
    break;

    case 10:
	startTick = _qnotes2ticks(seq, 1) + 1;
	endTick = _qnotes2ticks(seq, 3) + 1;
	numLoops = 16;
    break;

    case 11:
	startTick = 0;
	endTick = 0x7fffffff;
	numLoops = -1;
    break;

    default:
	PRINTF("\tdone\n");
	return;
    }

    alUSNewMarker (seq, &zeroMkr, 0);
    alUSNewMarker (seq, &startMkr, startTick);
    alUSNewMarker (seq, &endMkr, endTick);
    alUSSetLoc (seq, &zeroMkr);
    alSeqpLoop (seqp, &startMkr, &endMkr, numLoops);

    PRINTF("\tloop %d times from %d to %d ticks, mkrs [%d, %d)\n",
	   numLoops, startTick, endTick, startMkr.lastTicks, endMkr.curTicks);

    alUSPPlay (seqp);
}


static u32
_qnotes2ticks (ALUSeq *seq, u32 qnotes)
{
    return _DivS32ByF32 (qnotes, seq->qnpt);
}


/*
  This routine safely divides a signed 32-bit integer
  by a floating point value.  It avoids overflow by using
  a double to store the result and then before truncating
  to an integer it compares the result to the limit and
  limits it on overflow.  Underflow is handled automatically
  by the CPU which limits the value to zero.

  Presently this routine is  used to divide a time in usecs
  by a pitch ratio. Since the time could be a very large number,
  very small pitch ratios can cause the reult to overflow,
  causing a floating point exception.
*/
static
s32 _DivS32ByF32 (s32 i, f32 f)
{
    #define INT_MAX         2147483647      /* Should be in a limits.h file. */

    f64	rd;
    int	ri;

    assert(f!=0);	/* Caller must make sure we do not divide by zero! */

    rd = i/f;		/* Store result as a double to avoid overflow. */
    
    if (rd > INT_MAX)	/* Limit the value if necessary. */
	ri = INT_MAX;
    else
	ri = rd;

    return ri;
}


static void
_GetLoopTestSequence (void)
{
    #define LOOP_SEQ_NUM	0

    ALSeqFile *	testSeqFile;
    u8  *	romData;	
    s32 	len;
    
    /**** Load the sequence file header and data from ROM ****/
    testSeqFile = alHeapAlloc(&gHeapRec, 1, 4);
    romCopy(_testseqSegmentRomStart, (u8 *)testSeqFile, 4);    
    len = 4 + testSeqFile->seqCount*sizeof(ALSeqData);
    testSeqFile = alHeapAlloc(&gHeapRec, 1, len);
    romCopy(_testseqSegmentRomStart, (u8 *)testSeqFile, len);
    
    alSeqFileNew(testSeqFile, (u8 *) _testseqSegmentRomStart);

    romData = testSeqFile->seqArray[LOOP_SEQ_NUM].offset;
    len = testSeqFile->seqArray[LOOP_SEQ_NUM].len;

    if (len & 0x1)
	len++;

    romCopy (romData, gSeqData, len);

    alUSNew (gSeq, gSeqData, len);
}