audio.c 10.6 KB
/*====================================================================
 * audio.c
 *
 * Synopsis:
 *
 * This code implements the application audio thread.
 * 
 * Copyright 1993, Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics,
 * Inc.; the contents of this file may not be disclosed to third
 * parties, copied or duplicated in any form, in whole or in part,
 * without the prior written permission of Silicon Graphics, Inc.
 *
 * RESTRICTED RIGHTS LEGEND:
 * Use, duplication or disclosure by the Government is subject to
 * restrictions as set forth in subdivision (c)(1)(ii) of the Rights
 * in Technical Data and Computer Software clause at DFARS
 * 252.227-7013, and/or in similar or successor clauses in the FAR,
 * DOD or NASA FAR Supplement. Unpublished - rights reserved under the
 * Copyright Laws of the United States.
 *====================================================================*/

/*---------------------------------------------------------------------*
        Copyright (C) 1998 Nintendo. (Originated by SGI)
        
        $RCSfile: audio.c,v $
        $Revision: 1.1.1.1 $
        $Date: 2002/05/02 03:27:22 $
 *---------------------------------------------------------------------*/

#include <ultra64.h>
#include <ultralog.h>

#include <assert.h>

#include "audio.h"
#include <sched.h>
#include "space.h"

extern OSSched sc;
extern OSPiHandle	*handler;

/*
 * private typedefs and defines
 */
#define	FRAME_SIZE	        544     

/*
#define	FRAME_SIZE	        736
*/
#define MIN_FRAME_SIZE          (FRAME_SIZE - 16)
#define EXTRA_SAMPLES           80
#define MAX_BUFFERS             400

#define MAX_MESGS               8
#define MAX_BUFFER_LENGTH       1024
#define NUM_AUDIO_BUFFERS       3
#define NUM_DMA_BUFFERS         4

#define QUIT_MSG                10

/*
 * ### get rid of these
 */
extern int verbose;
extern OSLog *log;
extern int logging;

typedef union {    

    struct {
        short   type;
    } gen;
    
    struct {
        short type;
        struct AudioInfo_s *info;
    } done;
    
    OSScMsg     app;
    
} AudioMsg;

typedef struct AudioInfo_s {
    Acmd        cmds[2048];     /* FIRST so struct will be long word aligned */
    short       data[2048];     /* Output data: stereo at 768 samps/frame */
    short       frameSamples;   /* # of samples synthesized in this frame */
    OSScTask    task;           /* scheduler structure */
    AudioMsg    msg;            /* completion message */
} AudioInfo;

typedef struct {
    AudioInfo   *audioInfo[NUM_AUDIO_BUFFERS];
    OSThread    thread;
    OSMesgQueue audioFrameMsgQ;
    OSMesg      audioFrameMsgBuf[MAX_MESGS];
    OSMesgQueue audioReplyMsgQ;
    OSMesg      audioReplyMsgBuf[MAX_MESGS];
    ALGlobals   g;
} AMAudioMgr;

typedef struct {
    u32         addr;
    u32         len;
    u32         size;
    char        *ptr;
} AMDMABuffer;

typedef struct {
    u32         nBuffers;
    u32         currentBuffer;
    AMDMABuffer   buffers[NUM_DMA_BUFFERS];
} AMDMAState;

AMAudioMgr      __am;

static u64	audioStack[STACKSIZE/8];

int             nextDMA = 0;
int             curBuf = 0;

/*
 * Queues and storage for use with PI manager
 */
OSIoMesg	audDMAIOMesgBuf[MAX_BUFFERS];
OSMesgQueue	audDMAMessageQ;
OSMesg		audDMAMessageBuf[MAX_BUFFERS];

/*
 * Heap for storage allocation
 */
extern ALHeap   hp;

/*
 * private routines
 */
static void __amMain(void *arg);
static s32  __amDMA(s32 addr, s32 len, void *state);
static ALDMAproc __amDmaNew(AMDMAState **state);
static void __amHandleFrameMsg(AudioInfo *);
static void __amHandleDoneMsg(AudioInfo *);
static void __clearAudioDMA(int nB);

/***********************************************************************
 * Audio Manager API
 **********************************************************************/
void amCreateAudioMgr(ALSynConfig *c, OSPri pri)
{
    int i;
    c->dmaproc    = __amDmaNew;

    alInit(&__am.g, c);

    /*
     * initialize the done messages
     */
    for (i = 0; i < NUM_AUDIO_BUFFERS; i++) {
        __am.audioInfo[i] = (AudioInfo *)alHeapAlloc(c->heap, 1,
                                                     sizeof(AudioInfo));
        __am.audioInfo[i]->msg.done.type = OS_SC_DONE_MSG;
        __am.audioInfo[i]->msg.done.info = __am.audioInfo[i];    
    }    
    
    osCreateMesgQueue(&__am.audioReplyMsgQ, __am.audioReplyMsgBuf, MAX_MESGS);
    osCreateMesgQueue(&__am.audioFrameMsgQ, __am.audioFrameMsgBuf, MAX_MESGS);
    osCreateMesgQueue(&audDMAMessageQ, audDMAMessageBuf, MAX_BUFFERS);

    osCreateThread(&__am.thread, 3, __amMain, 0,
		   (void *)(audioStack+STACKSIZE/8), pri);
    osStartThread(&__am.thread);
}

/***********************************************************************
 * Audio Manager implementation
 **********************************************************************/
static void __amMain(void *arg) 
{
    int buf     = 0;
    int bufCnt  = 0;
    int frame   = 0;
    int done    = 0;
    AudioMsg    *msg;
    OSScClient  client;
    int         rv;
    
    /*
     * initialize the Audio Library
     */

    osScAddClient(&sc, &client, &__am.audioFrameMsgQ);
    
    while (!done) {
        (void) osRecvMesg(&__am.audioFrameMsgQ, (OSMesg *)&msg, OS_MESG_BLOCK);

        switch (msg->gen.type) {
            case (OS_SC_RETRACE_MSG):
                frame++;
                
                if (bufCnt < NUM_AUDIO_BUFFERS) {
                    __clearAudioDMA(nextDMA);
                    nextDMA = 0;
                    __amHandleFrameMsg(__am.audioInfo[buf % 3]);
                   
                    bufCnt++;
                    buf++; 
                    curBuf ^= 1;
                }
                
                rv = osRecvMesg(&__am.audioReplyMsgQ, (OSMesg *)&msg,
                                OS_MESG_BLOCK);
                if (rv != -1) {
                    __amHandleDoneMsg(msg->done.info);
                    bufCnt--;
                }
                
                break;

            case (QUIT_MSG):
                done = 1;
                break;

            default:
                break;
                
        }        
    }
    
    alClose(&__am.g);

}

static void __amHandleFrameMsg(AudioInfo *info)
{
    char *name = "audio";
    s16 *audioPtr;
    Acmd *cmdp;
    s32 cmdLen;
    int samplesLeft = 0;
    OSScTask *t;
    static int count = 0;
    audioPtr = (s16 *) osVirtualToPhysical(info->data);

    /*
     * How many samples I need for this frame to keep the DAC full
     */
    samplesLeft = IO_READ(AI_LEN_REG)>>2;
    
    info->frameSamples = 16 + (FRAME_SIZE - samplesLeft + EXTRA_SAMPLES)& ~0xf;

    if (info->frameSamples < MIN_FRAME_SIZE)
        info->frameSamples = MIN_FRAME_SIZE;

    cmdp = alAudioFrame(info->cmds, &cmdLen, audioPtr, info->frameSamples);

    t = &info->task;
    
    t->next      = 0;                    /* paranoia */
    t->msgQ      = &__am.audioReplyMsgQ; /* reply to when finished */
    t->msg       = (OSMesg)&info->msg;   /* reply with this message */
    t->flags     = OS_SC_NEEDS_RSP;
    
    t->list.t.data_ptr    = (u64 *) info->cmds;
    t->list.t.data_size   = (cmdp - info->cmds) * sizeof(Acmd);
    t->list.t.type  = M_AUDTASK;
    t->list.t.ucode_boot = (u64 *)rspbootTextStart;
    t->list.t.ucode_boot_size =
        ((int) rspbootTextEnd - (int) rspbootTextStart);
    t->list.t.flags  = OS_TASK_DP_WAIT;
    t->list.t.flags  = 0;
    t->list.t.ucode = (u64 *) aspMainTextStart;
    t->list.t.ucode_data = (u64 *) aspMainDataStart;
    t->list.t.ucode_data_size = SP_UCODE_DATA_SIZE;
    t->list.t.yield_data_ptr = NULL;
    t->list.t.yield_data_size = NULL;
        
    osSendMesg(osScGetCmdQ(&sc), (OSMesg) t, OS_MESG_BLOCK);
}

static void __amHandleDoneMsg(AudioInfo *info) 
{
    int samplesLeft;
    static int firstTime = 1;
    s32	rval;

    samplesLeft = IO_READ(AI_LEN_REG)>>2;
    if (samplesLeft == 0 && !firstTime) {
        PRINTF("audio: ai out of samples\n");    
	firstTime = 0;
    }

    rval = osAiSetNextBuffer(info->data, info->frameSamples<<2);
    if (rval < 0)
	PRINTF("audio: osAiSetNextBuffer failed\n");

}


    
s32 __amDMA(s32 addr, s32 len, void *state)
{
    void        *freeBuffer;
    AMDMAState  *dState = state;
    s32         delta = 0;
    u32         bStartAddr;
    u32         bEndAddr;
    AMDMABuffer *dBuff = &dState->buffers[dState->currentBuffer];
    OSMesg      dummyMesg;
    
    /*
     * Is it in the last buffer
     */
    
    bStartAddr = (u32) dBuff->addr;
    bEndAddr = (u32) bStartAddr + dBuff->len;

    if ((addr >= bStartAddr) && (addr+len <= bEndAddr)){
        freeBuffer = dBuff->ptr + addr - dBuff->addr;
    } else{
        if (++dState->currentBuffer >= dState->nBuffers )
            dState->currentBuffer = 0;        
        dBuff = &dState->buffers[dState->currentBuffer];            
        freeBuffer = dBuff->ptr;
        delta = addr & 0x1;
        addr -= delta;
        len += delta;
        len += (len & 0x1);
        if (logging)
            osLogEvent(log, 9, 2, (int)addr, (int)len);
        
        if (len > MAX_BUFFER_LENGTH || len < 0) {
            rmonPrintf("PI: 0x%8.8x %d\n", addr, len);
            return (int) osVirtualToPhysical(freeBuffer) + delta;
        }
        dBuff->addr = addr;
        dBuff->len = dBuff->size;

        audDMAIOMesgBuf[nextDMA].hdr.pri      = OS_MESG_PRI_NORMAL;
        audDMAIOMesgBuf[nextDMA].hdr.retQueue = &audDMAMessageQ;
        audDMAIOMesgBuf[nextDMA].dramAddr     = freeBuffer;
        audDMAIOMesgBuf[nextDMA].devAddr      = (u32)addr;
        audDMAIOMesgBuf[nextDMA].size         = dBuff->size;

        osEPiStartDma(handler, &audDMAIOMesgBuf[nextDMA++], OS_READ);
    }
    return (int) osVirtualToPhysical(freeBuffer) + delta;
}

ALDMAproc __amDmaNew(AMDMAState **state)

{
    int         i;
    AMDMAState    *dState;
    
    dState = (AMDMAState *) alHeapAlloc(&hp, 1, sizeof(AMDMAState));
    dState->currentBuffer = 0;
    dState->nBuffers = NUM_DMA_BUFFERS;
    for (i=0; i<NUM_DMA_BUFFERS; i++){
        dState->buffers[i].ptr = alHeapAlloc(&hp, 1, MAX_BUFFER_LENGTH);
        dState->buffers[i].addr = 0;
        dState->buffers[i].len = 0;
        dState->buffers[i].size = MAX_BUFFER_LENGTH;
    }
    *state = (AMDMAState *) dState;
    return __amDMA;
}
    
static void __clearAudioDMA(int nBuffers)

{
    int i;
    OSIoMesg *iomsg;
    
    /*
     * Should not block here since if the correct number
     * of messages haven't arrived the application has a bug
     */
    for (i=0; i<nBuffers; i++) {
        if (osRecvMesg(&audDMAMessageQ, (OSMesg *)&iomsg,
                       OS_MESG_NOBLOCK) == -1)
            PRINTF("Dma not done\n");

        if (logging)
            osLogEvent(log, 17, 2, iomsg->devAddr, iomsg->size);

    }
}