shithub: sox

ref: 19122d5ba7476b1ab747119a2717f51eb597dc02
dir: /src/reverb.c/

View raw version
/*
 * August 24, 1998
 * Copyright (C) 1998 Juergen Mueller And Sundry Contributors
 * This source code is freely redistributable and may be used for
 * any purpose.  This copyright notice must be maintained. 
 * Lance Norskog And Sundry Contributors are not responsible for 
 * the consequences of using this software.
 */

/*
**      Echo effect. based on:
**
** echoplex.c - echo generator
**
** Copyright (C) 1989 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/


/*
 * Changes to old "echo.c" now called "reverb.c":
 *
 * The effect name changes from "echo" to "reverb" (see Guitar FX FAQ) for
 * the difference in its defintion.
 * The idea of the echoplexer is modified and enhanceb by an automatic
 * setting of each decay for realistic reverb.
 * Some bugs are fixed concerning xmalloc and fade-outs.
 * Added an output volume (gain-out) avoiding saturation or clipping.
 *
 *
 * Reverb effect for dsp.
 *
 * Flow diagram scheme for n delays ( 1 <= n <= MAXREVERB )
 *
 *        * gain-in  +---+                        * gain-out
 * ibuff ----------->|   |------------------------------------> obuff
 *                   |   |  * decay 1
 *                   |   |<------------------------+
 *                   | + |  * decay 2              |
 *                   |   |<--------------------+   |
 *                   |   |  * decay n          |   |
 *                   |   |<----------------+   |   |
 *                   +---+                 |   |   |
 *                     |      _________    |   |   |
 *                     |     |         |   |   |   |
 *                     +---->| delay n |---+   |   |
 *                     .     |_________|       |   |
 *                     .                       |   |
 *                     .      _________        |   |
 *                     |     |         |       |   |
 *                     +---->| delay 2 |-------+   |
 *                     |     |_________|           |
 *                     |                           |
 *                     |      _________            |
 *                     |     |         |           |
 *                     +---->| delay 1 |-----------+
 *                           |_________|
 *
 *
 *
 * Usage:
 *   reverb gain-out reverb-time delay-1 [ delay-2 ... delay-n ]
 *
 * Where:
 *   gain-out :  0.0 ...      volume
 *   reverb-time :  > 0.0 msec
 *   delay-1 ... delay-n :  > 0.0 msec
 *
 * Note:
 *   gain-in is automatically adjusted avoiding saturation and clipping of
 *   the output. decay-1 to decay-n are computed such that at reverb-time
 *   the input will be 60 dB of the original input for the given delay-1 
 *   to delay-n. delay-1 to delay-n specify the time when the first bounce
 *   of the input will appear. A proper setting for delay-1 to delay-n 
 *   depends on the choosen reverb-time (see hint).
 *
 * Hint:
 *   a realstic reverb effect can be obtained using for a given reverb-time "t"
 *   delays in the range of "t/2 ... t/4". Each delay should not be an integer
 *   of any other.
 *
*/

/*
 * libSoX reverb effect file.
 */

#include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
#include <math.h>
#include "sox_i.h"

#define REVERB_FADE_THRESH 10
#define DELAY_BUFSIZ ( 50 * SOX_MAXRATE )
#define MAXREVERBS 8

/* Private data for SKEL file */
typedef struct reverbstuff {
        int     counter;                        
        size_t  numdelays;
        float   *reverbbuf;
        float   in_gain, out_gain, time;
        float   delay[MAXREVERBS], decay[MAXREVERBS];
        size_t  samples[MAXREVERBS], maxsamples;
        sox_ssample_t pl, ppl, pppl;
} *reverb_t;

/*
 * Process options
 */
static int sox_reverb_getopts(sox_effect_t * effp, int n, char **argv) 
{
        reverb_t reverb = (reverb_t) effp->priv;
        int i;

        reverb->numdelays = 0;
        reverb->maxsamples = 0;

        if ( n < 3 )
          return sox_usage(effp);

        if ( n - 2 > MAXREVERBS )
        {
            sox_fail("reverb: to many dalays, use less than %i delays",
                        MAXREVERBS);
            return (SOX_EOF);
        }

        i = 0;
        sscanf(argv[i++], "%f", &reverb->out_gain);
        sscanf(argv[i++], "%f", &reverb->time);
        while (i < n) {
                /* Linux bug and it's cleaner. */
                sscanf(argv[i++], "%f", &reverb->delay[reverb->numdelays]);
                reverb->numdelays++;
        }
        return (SOX_SUCCESS);
}

/*
 * Prepare for processing.
 */
static int sox_reverb_start(sox_effect_t * effp)
{
        reverb_t reverb = (reverb_t) effp->priv;
        size_t i;

        reverb->in_gain = 1.0;

        if ( reverb->out_gain < 0.0 )
        {
                sox_fail("reverb: gain-out must be positive");
                return (SOX_EOF);
        }
        if ( reverb->out_gain > 1.0 )
                sox_warn("reverb: warnig >>> gain-out can cause saturation of output <<<");
        if ( reverb->time < 0.0 )
        {
                sox_fail("reverb: reverb-time must be positive");
                return (SOX_EOF);
        }
        for(i = 0; i < reverb->numdelays; i++) {
                reverb->samples[i] = reverb->delay[i] * effp->ininfo.rate / 1000.0;
                if ( reverb->samples[i] < 1 )
                {
                    sox_fail("reverb: delay must be positive!");
                    return (SOX_EOF);
                }
                if ( reverb->samples[i] > DELAY_BUFSIZ )
                {
                        sox_fail("reverb: delay must be less than %g seconds!",
                                DELAY_BUFSIZ / effp->ininfo.rate );
                        return(SOX_EOF);
                }
                /* Compute a realistic decay */
                reverb->decay[i] = (float) pow(10.0,(-3.0 * reverb->delay[i] / reverb->time));
                if ( reverb->samples[i] > reverb->maxsamples )
                    reverb->maxsamples = reverb->samples[i];
        }
        reverb->reverbbuf = (float *) xmalloc(sizeof (float) * reverb->maxsamples);
        for ( i = 0; i < reverb->maxsamples; ++i )
                reverb->reverbbuf[i] = 0.0;
        reverb->pppl = reverb->ppl = reverb->pl = 0x7fffff;             /* fade-outs */
        reverb->counter = 0;
        /* Compute the input volume carefully */
        for ( i = 0; i < reverb->numdelays; i++ )
                reverb->in_gain *= 
                        ( 1.0 - ( reverb->decay[i] * reverb->decay[i] ));
        return (SOX_SUCCESS);
}

/*
 * Processed signed long samples from ibuf to obuf.
 * Return number of samples processed.
 */
static int sox_reverb_flow(sox_effect_t * effp, const sox_ssample_t *ibuf, sox_ssample_t *obuf, 
                   sox_size_t *isamp, sox_size_t *osamp)
{
        reverb_t reverb = (reverb_t) effp->priv;
        size_t i = reverb->counter, j;
        float d_in, d_out;
        sox_ssample_t out;
        sox_size_t len = min(*isamp, *osamp);
        *isamp = *osamp = len;

        while (len--) {
                /* Store delays as 24-bit signed longs */
                d_in = (float) *ibuf++ / 256;
                d_in = d_in * reverb->in_gain;
                /* Mix decay of delay and input as output */
                for ( j = 0; j < reverb->numdelays; j++ )
                        d_in +=
reverb->reverbbuf[(i + reverb->maxsamples - reverb->samples[j]) % reverb->maxsamples] * reverb->decay[j];
                d_out = d_in * reverb->out_gain;
                out = SOX_24BIT_CLIP_COUNT((sox_ssample_t) d_out, effp->clips);
                *obuf++ = out * 256;
                reverb->reverbbuf[i] = d_in;
                i++;            /* FIXME need a % maxsamples here ? */
                i %= reverb->maxsamples;
        }
        reverb->counter = i;
        /* processed all samples */
        return (SOX_SUCCESS);
}

/*
 * Drain out reverb lines. 
 */
static int sox_reverb_drain(sox_effect_t * effp, sox_ssample_t *obuf, sox_size_t *osamp)
{
        reverb_t reverb = (reverb_t) effp->priv;
        float d_in, d_out;
        sox_ssample_t out, l;
        size_t i, j;
        sox_size_t done;
        sox_bool notfaded;

        i = reverb->counter;
        done = 0;
        /* drain out delay samples */
        do {
                d_in = 0;
                d_out = 0;
                for ( j = 0; j < reverb->numdelays; ++j )
                        d_in += 
reverb->reverbbuf[(i + reverb->maxsamples - reverb->samples[j]) % reverb->maxsamples] * reverb->decay[j];
                d_out = d_in * reverb->out_gain;
                out = SOX_24BIT_CLIP_COUNT((sox_ssample_t) d_out, effp->clips);
                obuf[done++] = out * 256;
                reverb->reverbbuf[i] = d_in;
                l = SOX_24BIT_CLIP_COUNT((sox_ssample_t) d_in, effp->clips);
                reverb->pppl = reverb->ppl;
                reverb->ppl = reverb->pl;
                reverb->pl = l;
                i = (i + 1) % reverb->maxsamples;
                notfaded = (abs(reverb->pl) + abs(reverb->ppl) + abs(reverb->pppl) > REVERB_FADE_THRESH);
        } while (done < *osamp && notfaded);
        reverb->counter = i;
        *osamp = done;
        return notfaded? SOX_SUCCESS : SOX_EOF;
}

/*
 * Clean up reverb effect.
 */
static int sox_reverb_stop(sox_effect_t * effp)
{
        reverb_t reverb = (reverb_t) effp->priv;

        free((char *) reverb->reverbbuf);
        reverb->reverbbuf = (float *) -1;   /* guaranteed core dump */
        return (SOX_SUCCESS);
}

static sox_effect_handler_t sox_reverb_effect = {
  "reverb",
  "gain-out reverb-time delay [ delay ... ]",
  SOX_EFF_LENGTH,
  sox_reverb_getopts,
  sox_reverb_start,
  sox_reverb_flow,
  sox_reverb_drain,
  sox_reverb_stop,
  NULL
};

const sox_effect_handler_t *sox_reverb_effect_fn(void)
{
    return &sox_reverb_effect;
}