reverb.c 10.1 KB
/*====================================================================
 * reverb.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>
#include <stdio.h>

/*
 * all-pass macro
 */
#define	ALL_PASS(in, out, coef) \
	aSetBuffer(ptr++, 0, 0, 0, outCount<<1); \
        aMix(ptr++, 0, coef, out, in); \
        aMix(ptr++, 0, (unsigned short)(-(coef)), in, out);

void init_lpfilter(ALLowPass *lp)
{
    int i;
    float a = lp->fc;

    lp->first = 1;
    for (i=0; i<8; i++)
	lp->fcvec.fccoef[i] = 0;
    
    lp->fcvec.fccoef[i++] = (short)(a * 16384);
    lp->fgain = (short)((1.0 - a) * 16384);

    for (; i<16; i++){
	a *= lp->fc;
	lp->fcvec.fccoef[i] = (short)(a * 16384);
    }
}

Acmd *_loadBuffer(ALFx *r, int sect, int buff, int count, Acmd *p);
Acmd *_saveBuffer(ALFx *r, int sect, int buff, int count, Acmd *p);
Acmd *_filterBuffer(ALFx *r, int sect, int buff, int count, Acmd *p);

/***********************************************************************
 * Reverb filter public interfaces
 ***********************************************************************/
void alReverbNew(ALFx *r, ALFxId id, short *fxmem, int size, float rate)
{
    int i,j, total_length = 0;

#define ms *(((int)rate/1000)&~0x7)

    r->id = id;

    switch ( id ) {
      case AL_FX_SMALLROOM:

	alFilterNew((ALFilter *) r, alSmallRoomPull, alReverbParam, AL_FX);

	r->section_count = 3;

	r->delay[0].length = 22 ms;		/* length of delay		*/
	r->delay[0].coef = 9830;		/* coefficient for delay	*/
	
	r->delay[1].length = 35 ms;
	r->delay[1].coef = 3276;
	
	r->delay[2].length = 66 ms;
	r->delay[2].coef = 10000;
	
	r->delay[0].base = fxmem;		/* base address of delay	*/
	r->delay[0].tap = r->delay[0].base;	/* current position of tap	*/
	r->delay[0].lpfilt.fc = 0;		/* coef for 2 pole low pass	*/
	
	r->delay[1].base = r->delay[0].base + r->delay[0].length;
	r->delay[1].tap = r->delay[1].base;
	r->delay[1].lpfilt.fc = 0;
	
	r->delay[2].base = r->delay[1].base + r->delay[1].length;
	r->delay[2].tap = r->delay[2].base;
	r->delay[2].lpfilt.fc = .25;

	total_length += r->delay[0].length;
	total_length += r->delay[1].length;
	total_length += r->delay[2].length;

	break;

      case AL_FX_BIGROOM:

	alFilterNew((ALFilter *) r, alSmallRoomPull, alReverbParam, AL_FX);

	r->section_count = 3;

	r->delay[0].length = 42 ms;
	r->delay[0].coef = 9830;
	
	r->delay[1].length = 55 ms;
	r->delay[1].coef = 9830;
	
	r->delay[2].length = 86 ms;
	r->delay[2].coef = 12000;
	
	r->delay[0].base = fxmem;
	r->delay[0].tap = r->delay[0].base;
	r->delay[0].lpfilt.fc = 0;
	
	r->delay[1].base = r->delay[0].base + r->delay[0].length;
	r->delay[1].tap = r->delay[1].base;
	r->delay[1].lpfilt.fc = 0;
	
	r->delay[2].base = r->delay[1].base + r->delay[1].length;
	r->delay[2].tap = r->delay[2].base;
	r->delay[2].lpfilt.fc = .25;

	total_length += r->delay[0].length;
	total_length += r->delay[1].length;
	total_length += r->delay[2].length;

	break;

      case AL_FX_ECHO:

	alFilterNew((ALFilter *) r, alEchoPull, alReverbParam, AL_FX);

	r->section_count = 1;

	r->delay[0].length = 180 ms;
	r->delay[0].coef = 0x6000;
	
	r->delay[0].base = fxmem;
	r->delay[0].tap = r->delay[0].base;
	r->delay[0].lpfilt.fc = 0;
	
	total_length += r->delay[0].length;

	break;

      default:
	break;
    }

    if (total_length > size) {
	/*
	 * total delay too long! zero coefficients to protect ears!
	 * FIXME: need a debug error ststement here
	 */
	for (i=0; i<r->section_count; i++)
	    r->delay[i].coef = 0;
	return;
    }
    
    /*
     * clear delay memory
     */
    for (i=0; i < r->section_count; i++){
	for (j=0; j < r->delay[i].length; j++){
	    r->delay[i].base[j] = 0;
	}
	init_lpfilter(&r->delay[i].lpfilt);
    }

    r->motion = AL_PLAYING; /* FIXME: is this what we want? */
}

Acmd *alSmallRoomPull(void *filter, short *outp, int outCount, int flags,
                     Acmd *p) 
{
    Acmd        *ptr = p;
    ALFx	*r = (ALFx *)filter;
    ALFilter    *source;
    int		i, after_end, before_end;
    short	*prev_ptr, *end_ptr;

    /*
     * pull channels going into this effect first
     */
    if (source = r->filter.source) {
	ptr = (*source->handler)(source, outp, outCount, flags, p);
    } else {
	/* error */
    }

    /*
     * two all-passes in series inside a comb filter with a low-pass
     */
    if (r->motion == AL_PLAYING) {
	/*
	 * sum inputs attenuated by 3db
	 */
	aSetBuffer(ptr++, 0, 0, 0, outCount<<1);
	aMix(ptr++, 0, 0xda83, AL_AUX_L_OUT, AL_AUX_L_OUT);
	aMix(ptr++, 0, 0x5a82, AL_AUX_R_OUT, AL_AUX_L_OUT);

	ptr = _loadBuffer(r, 2, AL_AUX_R_OUT, outCount, ptr);
	ptr = _filterBuffer(r, 2, AL_AUX_R_OUT, outCount, ptr);
	aMix(ptr++, 0, r->delay[2].coef, AL_AUX_R_OUT, AL_AUX_L_OUT);

	ptr = _loadBuffer(r, 0, AL_AUX_R_OUT, outCount, ptr);
	ALL_PASS(AL_AUX_L_OUT, AL_AUX_R_OUT, r->delay[0].coef);
	ptr = _saveBuffer(r, 0, AL_AUX_L_OUT, outCount, ptr);

	ptr = _loadBuffer(r, 1, AL_AUX_L_OUT, outCount, ptr);
	ALL_PASS(AL_AUX_R_OUT, AL_AUX_L_OUT, r->delay[1].coef);
	ptr = _saveBuffer(r, 1, AL_AUX_R_OUT, outCount, ptr);

	ptr = _saveBuffer(r, 2, AL_AUX_L_OUT, outCount, ptr);
	aDMEMMove(ptr++, AL_AUX_L_OUT, AL_AUX_R_OUT, outCount<<1);

    }

    return ptr;
}

Acmd *alEchoPull(void *filter, short *outp, int outCount, int flags,
                     Acmd *p) 
{
    Acmd        *ptr = p;
    ALFx	*r = (ALFx *)filter;
    ALFilter    *source;
    int		i, after_end, before_end;
    short	*curr_ptr;
    short	*prev_ptr, *end_ptr;

    /*
     * pull channels going into this effect first
     */
    if (source = r->filter.source) {
	ptr = (*source->handler)(source, outp, outCount, flags, p);
    } else {
	/* error */
    }

    if (r->motion == AL_PLAYING) {
	/*
	 * sum inputs attenuated by 3db
	 */
	aSetBuffer(ptr++, 0, AL_AUX_R_OUT, AL_AUX_L_OUT, outCount<<1);
	aMix(ptr++, 0, 0xda83, AL_AUX_L_OUT, AL_AUX_L_OUT);
	aMix(ptr++, 0, 0x5a82, AL_AUX_R_OUT, AL_AUX_L_OUT);

	ptr = _loadBuffer(r, 0, AL_AUX_R_OUT, outCount, ptr);
	aMix(ptr++, 0, r->delay[0].coef, AL_AUX_R_OUT, AL_AUX_L_OUT);
	ptr = _saveBuffer(r, 0, AL_AUX_L_OUT, outCount, ptr);

	aDMEMMove(ptr++, AL_AUX_R_OUT, AL_AUX_L_OUT, outCount<<1);
    }

    return ptr;
}

int alReverbParam(void *filter, int paramID, void *param)
{
    ALFilter    *f = (ALFilter *) filter;
    ALFx	*r = (ALFx *) filter;
    int pp = (int) param;
    
    switch (paramID) {

        case (AL_FILTER_SET_SOURCE):
            f->source = (ALFilter *) param;
            break;
	  
      case (AL_FILTER_SET_DECAY):
	switch (r->id) {
	  case AL_FX_SMALLROOM:
	  case AL_FX_BIGROOM:
	    r->delay[2].coef = (short)param;
	    break;
	  case AL_FX_ECHO:
	    r->delay[0].coef = (short)param;
	    break;
	  default:
	    break;
	}
	break;
	  
      case (AL_FILTER_SET_FC):
	switch (r->id) {
	  case AL_FX_SMALLROOM:
	  case AL_FX_BIGROOM:
	    r->delay[2].lpfilt.fc = (short)param;
	    break;
	  case AL_FX_ECHO:
	    r->delay[0].lpfilt.fc = (short)param;
	    break;
	  default:
	    break;
	}
	break;
	  
        case (AL_FILTER_START): 
            r->motion = AL_PLAYING;
            break;
            
        case (AL_FILTER_RESET):
            r->motion = AL_STOPPED;
            if (f->source)
                (*f->source->setParam)(f->source, AL_FILTER_RESET, param);
            break;

        default:
            /* ??? */
            break;
    }
    return 0;
}

Acmd *_loadBuffer(ALFx *r, int sect, int buff, int count, Acmd *p)
{
    Acmd	*ptr = p;
    int		after_end, before_end;
    short 	*curr_ptr, *end_ptr;

    curr_ptr = r->delay[sect].tap + count;
    end_ptr = r->delay[sect].base + r->delay[sect].length;
    if (curr_ptr > end_ptr) {
	after_end = curr_ptr - end_ptr;
	before_end = count - after_end;

	aSetBuffer(ptr++, 0, buff, 0, before_end<<1);
	aLoadBuffer(ptr++, osVirtualToPhysical(r->delay[sect].tap));
	aSetBuffer(ptr++, 0, buff+(before_end<<1), 0, after_end<<1);
	aLoadBuffer(ptr++, osVirtualToPhysical(r->delay[sect].base));

    } else {
	aSetBuffer(ptr++, 0, buff, 0, count<<1);
	aLoadBuffer(ptr++, osVirtualToPhysical(r->delay[sect].tap));
    }

    return ptr;

}

Acmd *_saveBuffer(ALFx *r, int sect, int buff, int count, Acmd *p)
{
    Acmd	*ptr = p;
    int		after_end, before_end;
    short 	*curr_ptr, *end_ptr;

    curr_ptr = r->delay[sect].tap + count;
    end_ptr = r->delay[sect].base + r->delay[sect].length;
    if (curr_ptr > end_ptr) {
	after_end = curr_ptr - end_ptr;
	before_end = count - after_end;

	aSetBuffer(ptr++, 0, 0, buff, before_end<<1);
	aSaveBuffer(ptr++, osVirtualToPhysical(r->delay[sect].tap));
	aSetBuffer(ptr++, 0, 0, buff+(before_end<<1), after_end<<1);
	aSaveBuffer(ptr++, osVirtualToPhysical(r->delay[sect].base));
	r->delay[sect].tap = r->delay[sect].base + after_end;

    } else {
	aSetBuffer(ptr++, 0, 0, buff, count<<1);
	aSaveBuffer(ptr++, osVirtualToPhysical(r->delay[sect].tap));
	r->delay[sect].tap = curr_ptr;
    }

    return ptr;

}

Acmd *_filterBuffer(ALFx *r, int sect, int buff, int count, Acmd *p)
{
    Acmd	*ptr = p;

    aSetBuffer(ptr++, 0, buff, buff, count<<1);
    aLoadADPCM(ptr++, sizeof(POLEF_STATE),
	       osVirtualToPhysical(&r->delay[sect].lpfilt.fcvec.fccoef));
    if (r->delay[sect].lpfilt.first){
	r->delay[sect].lpfilt.first = 0;
	aPoleFilter(ptr++, A_INIT, r->delay[sect].lpfilt.fgain,
		    osVirtualToPhysical(&r->delay[sect].lpfilt.fstate.astate));
    } else {
	aPoleFilter(ptr++, 0, r->delay[sect].lpfilt.fgain,
		    osVirtualToPhysical(&r->delay[sect].lpfilt.fstate.astate));
    }

    return ptr;

}