adpcm.c 9.86 KB
/*====================================================================
 * adpcm.c
 *
 * 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.
 *====================================================================*/

#include <libaudio.h>
#include "synthInternals.h"
#include <em.h>
#include <os.h>

#define ADPCMFBYTES      9
#define LFSAMPLES        4

void alAdpcmNew(ALADPCMFilter *f) 
{
    /*
     * init filter superclass
     */

    alFilterNew((ALFilter *) f, alAdpcmPull, alAdpcmParam, AL_ADPCM);
    
    /*
     * init the adpcm state
     */
    f->lastsam = 0;
    f->first = 1;
    f->memin = 0;
    f->current = 0;
}

Acmd *alAdpcmPull(void *filter, short *outp, int outCount, int flags, Acmd *p) 
{
    Acmd        *ptr = p;
    short       inp;
    int         tsam;
    int         nsam;
    int         nframes;
    int         nbytes;
    int         dramLoc;
    int         dramAlign;
    int         overFlow;
    int         startZero;
    int         nOver;
    int         nSam;
    int         op;
    int         nLeft;
    int         Bend;
    ALADPCMFilter *f = (ALADPCMFilter *)filter;
    ALFilter *a = (ALFilter *) filter;

    /* location of the input
     * 
     * Should do something about more clever buffer managment?
     */

    inp = AL_DECODER_IN;

    if (f->loop.count)
        if ((outCount + f->sample > f->loop.end) && (f->loop.count>0)){
            
            /*
             * I'm crossing the end point, break construction in two
             */
            nsam = f->loop.end - f->sample;

            /*
             * Check for leftovers from the last run
             */
            if (f->lastsam)
                tsam = nsam - ADPCMFSIZE + f->lastsam;
            else
                tsam = nsam;
            /*
             * If tsam is less than zero then I don't generate new samples
             * in the adpcm decode but I reload the ones calculated from the
             * last frame.
             */
            if (tsam<0) tsam = 0;

            nframes = (tsam+ADPCMFSIZE-1)>>LFSAMPLES;
            nbytes =  nframes*ADPCMFBYTES;

            /*
             * Callback to application to get the required input
             */
            dramLoc = (f->dma)(f->memin, nbytes);

            /*
             * Make sure enough is loaded into DMEM to take care
             * of 8 byte alignment
             */
            dramAlign = dramLoc & 0x7;
            nbytes += dramAlign;

            aSetBuffer(ptr++, 0, inp, 0, nbytes + 8 - (nbytes & 0x7));
            aLoadBuffer(ptr++, dramLoc - dramAlign);
            aLoadADPCM(ptr++, f->bookSize,
                       osVirtualToPhysical(f->table->book->book));
            aSetBuffer(ptr++, 0, inp + dramAlign, *outp, tsam<<1);
            aADPCMdec(ptr++, 0, 1,
                      osVirtualToPhysical(f->state[f->current].astate));

            /*
             * Put next one after the end of the last lot.
             */
            op = *outp + ((nframes+1)<<(LFSAMPLES+1));

            /*
             * Fix up output pointer, which will be used as the input pointer
             * by the following module.
             */
            if (f->lastsam)
                *outp += (f->lastsam<<1);
            else
                *outp += (ADPCMFSIZE<<1);

            /*
             * Actual end of buffer in DMEM.
             */
            Bend = *outp + (nsam<<1);
            
            /*
             * Now fix up state info to reflect start point
             */
            f->current = (++f->current) % AL_MAX_ADPCM_STATES;
            alCopy(f->loop.state.astate, f->state[f->current].astate,
                   sizeof(ADPCM_STATE));
            f->lastsam = f->loop.start &0xf;
            f->memin = (int) f->table->base + ADPCMFBYTES *
                ((int) (f->loop.start>>LFSAMPLES) + 1);
            f->sample = f->loop.start;

            /*
             * -1 is loop forever
             */
            if (f->loop.count != -1)
                f->loop.count--;
            
            /*
             * What's left to compute.
             */
            outCount -= nsam;
            
            /*
             * Do the next section, same as last.
             */
            tsam = outCount - ADPCMFSIZE + f->lastsam;  
            if (tsam<0) tsam = 0;
            nframes = (tsam+ADPCMFSIZE-1)>>LFSAMPLES;
            nbytes =  nframes*ADPCMFBYTES;
            /*
             * Callback to application to get the required input
             */
            dramLoc = (f->dma)(f->memin, nbytes);
            /*
             * Make sure enough is loaded into DMEM to take care
             * of 8 byte alignment
             */
            dramAlign = dramLoc & 0x7;
            nbytes += dramAlign;
            
            aSetBuffer(ptr++, 0, inp, 0, nbytes + 8 - (nbytes & 0x7));
            aLoadBuffer(ptr++, dramLoc - dramAlign);
            aLoadADPCM(ptr++, f->bookSize,
                       osVirtualToPhysical(f->table->book->book));
            aSetBuffer(ptr++, 0, inp + dramAlign, op, tsam<<1);
            aADPCMdec(ptr++, 0, 1,
                      osVirtualToPhysical(f->state[f->current].astate));

            /*
             * Merge the two sections in DMEM.
             */
            aDMEMMove(ptr++, op+(f->lastsam<<1), Bend, outCount<<1);

            f->lastsam = (outCount + f->lastsam) & 0xf;
            f->sample += outCount;
            f->memin += ADPCMFBYTES*nframes;    

            return ptr;
        }

    /*
     * The unlooped case, which is executed most of the time
     */
    if (f->lastsam)
        nLeft = ADPCMFSIZE - f->lastsam;
    else
        nLeft = 0;
    tsam = outCount - nLeft;
    if (tsam<0) tsam = 0;
    nframes = (tsam+ADPCMFSIZE-1)>>LFSAMPLES;
    nbytes =  nframes*ADPCMFBYTES;
    nSam = nframes<<LFSAMPLES;

    /*
     * overFlow is the number of bytes past the end
     * of the bitstream I try to generate
     */
    overFlow = f->memin + nbytes - ((int) f->table->base + f->table->len);
    if (overFlow < 0)
        overFlow = 0;
    nOver = (overFlow/ADPCMFBYTES)<<LFSAMPLES;
    if (nOver > nSam + nLeft)
        nOver = nSam + nLeft;
    
    nbytes -= overFlow;

    if ((nOver - (nOver & 0xf))< outCount){
        dramLoc = (f->dma)(f->memin, nbytes);
        dramAlign = dramLoc & 0x7;
        nbytes += dramAlign;
    
        aSetBuffer(ptr++, 0, inp, 0, nbytes + 8 - (nbytes & 0x7));
        aLoadBuffer(ptr++, dramLoc - dramAlign);
        aLoadADPCM(ptr++, f->bookSize,
                   osVirtualToPhysical(f->table->book->book));

        aSetBuffer(ptr++, 0, inp + dramAlign, *outp, (nSam-nOver)<<1);
        if (f->first){
            f->first = 0;
            aADPCMdec(ptr++, A_INIT, 1,
                      osVirtualToPhysical(f->state[f->current].astate));
        }
        else
            aADPCMdec(ptr++, 0, 1,
                      osVirtualToPhysical(f->state[f->current].astate));
    
        if (f->lastsam)
            *outp += (f->lastsam<<1);
        else
            *outp += (ADPCMFSIZE<<1);

        f->lastsam = (outCount + f->lastsam) & 0xf;
        f->sample += outCount;
        f->memin += ADPCMFBYTES*nframes;    
    } else
        dramAlign = 0;

    /*
     * Put zeros in if necessary
     */
    if (nOver){
        f->lastsam = 0;
        startZero = (nLeft + nSam - nOver)<<1;
        aClearBuffer(ptr++, startZero + *outp, nOver<<1);
    }
    return ptr;
}

int
alAdpcmParam(void *filter, int paramID, void *param)
{
    ALADPCMFilter *a = (ALADPCMFilter *) filter;
    ALFilter *f = (ALFilter *) filter;
    
    switch (paramID) {
        case (AL_FILTER_SET_SOURCE):
            f->source = (ALFilter *) param;
            break;

        case (AL_FILTER_SET_WAVETABLE):
            a->table = (ALWaveTable *) param;
            /*
             * Make sure the table length is an integer number of
             * frames
             */
            a->table->len = ADPCMFBYTES * ((int) (a->table->len/ADPCMFBYTES));
            a->memin = (int) a->table->base;
            a->sample = 0;
            a->current = 0;
            a->bookSize = 2*a->table->book->order*
                a->table->book->npredictors*ADPCMVSIZE;
            if (a->table->loop) {
                a->loop.start = a->table->loop->start;
                a->loop.end = a->table->loop->end;
                a->loop.count = a->table->loop->count;
                alCopy(a->table->loop->state.astate, a->loop.state.astate,
                       sizeof(ADPCM_STATE));
            } else {
                a->loop.start = a->loop.end = a->loop.count = 0;
            }
            break;
            
        case (AL_FILTER_SET_DMA_PROC):
            a->dma = (ALDMAproc) param;
            break;

        case (AL_FILTER_SKIP_LOOP):
            a->loop.count = 0;
            break;
            
        case (AL_FILTER_RESET):
            a->memin  = (int) a->table->base;
            a->lastsam = 0;
            a->first   = 1;
            a->current = 0;
            a->sample = 0;
            if (a->table->loop)
                a->loop.count = a->table->loop->count;
            if (f->source)
                (*f->source->setParam)(f->source, AL_FILTER_RESET, param);
            break;
            
        default:
            if (f->source)
                (*f->source->setParam)(f, paramID, param);
            break;
    }
    return 0;
}