octal-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Octal-dev] soxechos.c - SoX echos.c converted to Octal


From: Neil Nelson
Subject: [Octal-dev] soxechos.c - SoX echos.c converted to Octal
Date: Wed, 28 Jun 2000 17:57:33 -0700

Although the name soxechos.c is similar to soxecho.c just released,
this program has a decaying reverberation effect; whereas soxecho.c
provided only 1 echo for each of the 7 echo parameter pairs used.

Neil Nelson
/*   SOXECHOS.C
*
* This code has been adapted from the SoX echos.c code:
*
 * 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. 
 * Juergen Mueller And Sundry Contributors are not responsible for 
 * the consequences of using this software.
*
* Conversion to Octal made by:
*
* Copyright 2000 Neil Nelson <address@hidden> June 28, 2000.
*
* This software is distributed under the terms of the
* GNU General Public License (GPL). Read the included file
* COPYING for more information.
*
*
* From SoX:
* Echos effect for dsp.
* 
* Flow diagram scheme for n delays ( 1 <= n <= MAX_ECHOS ):
*
*                                                    * gain-in  ___
* ibuff --+--------------------------------------------------->|   |
*         |                                          * decay 1 |   |
*         |               +----------------------------------->|   |
*         |               |                          * decay 2 | + |
*         |               |             +--------------------->|   |
*         |               |             |            * decay n |   |
*         |    _________  |  _________  |     _________   +--->|___|
*         |   |         | | |         | |    |         |  |      | 
*         +-->| delay 1 |-+-| delay 2 |-+...-| delay n |--+      | * gain-out
*             |_________|   |_________|      |_________|         |
*                                                                +----->obuff
*
* Usage: 
*   echos gain-in gain-out decay-1 delay-1 [decay-2 delay-2 ... decay-n delay-n 
]
*
* Where:
*   gain-in, decay-1 ... decay-n :  0.0 ... 1.0      volume
*   gain-out :  0.0 ...      volume
*   delay-1 ... delay-n :  > 0.0 msec
*
* Note:
*   when decay is close to 1.0, the samples can begin clipping and the output
*   can saturate! 
*
* Hint:
*   1 / out-gain > gain-in ( 1 + decay-1 + ... + decay-n )
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "util.h"
#include "machine.h"

#define DYN_RANGE 10
#define MAX_ECHOS 7
#define MAX_DELAY 750 // msec

/*  The parameters are:  gain_in, gain_out, decay, delay, ... 6 more times */
enum {
   ix_gain_in, ix_gain_out,
   ix_sxedecay, ix_sxedelay,   
   ix_sxedecay_1, ix_sxedelay_1,   
   ix_sxedecay_2, ix_sxedelay_2,   
   ix_sxedecay_3, ix_sxedelay_3,   
   ix_sxedecay_4, ix_sxedelay_4,   
   ix_sxedecay_5, ix_sxedelay_5,   
   ix_sxedecay_6, ix_sxedelay_6  
} param_index;

param_spec sxe_params[] = {
   /*  Param 1: gain_in. */
   {
   small,
   slider,
   "Gain In",
   "Gain In",
   0,           /* 0 gain */
   255,         /* No attenuation */
   200          /* Approx. 200/255 of dynamic range */
   },

   /*  Param 2: gain_out. */
   {
   small,
   slider,
   "Gain Out",
   "Gain Out",
   0,           /* 0 gain */
   255,         /* No attenuation */
   200          /* Approx. 200/255 of dynamic range */
   },

   /*  Param 3: sxedecay[0]. */
   {
   small,
   slider,
   "Decay 0",
   "Decay of Delay 0.",
   0,
   255,         /* 1 */
   100          /* Approx. 100/255 of dynamic range */
   },

   /*  Param 4: sxedelay[0]. */
   {
   small,
   slider,
   "Delay 0",
   "Delay 0 time: 0 to maximum.",
   0,           /* 0 msec. delay  */
   255,         /* Highest delay of range */
   255          /* 255/255 of MAX_DELAY msec */
   },

   /*  Param 5: sxedecay[1]. */
   {
   small,
   slider,
   "Decay 1",
   "Decay of Delay 1.",
   0,
   255,         /* 1 */
   100          /* Approx. 100/255 of dynamic range */
   },

   /*  Param 6: sxedelay[1]. */
   {
   small,
   slider,
   "Delay 1",
   "Delay 1 time: 0 to maximum.",
   0,           /* 0 msec. delay  */
   255,         /* Highest delay of range */
   210          /* 210/255 of MAX_DELAY msec */
   },

   /*  Param 7: sxedecay[2]. */
   {
   small,
   slider,
   "Decay 2",
   "Decay of Delay 2.",
   0,
   255,         /* 1 */
   100          /* Approx. 100/255 of dynamic range */
   },

   /*  Param 8: sxedelay[2]. */
   {
   small,
   slider,
   "Delay 2",
   "Delay 2 time: 0 to maximum.",
   0,           /* 0 msec. delay  */
   255,         /* Highest delay of range */
   170          /* 170/255 of MAX_DELAY msec */
   },

   /*  Param 9: sxedecay[3]. */
   {
   small,
   slider,
   "Decay 3",
   "Decay of Delay 3.",
   0,
   255,         /* 1 */
   100          /* Approx. 100/255 of dynamic range */
   },

   /*  Param 10: sxedelay[3]. */
   {
   small,
   slider,
   "Delay 3",
   "Delay 3 time: 0 to maximum.",
   0,           /* 0 msec. delay  */
   255,         /* Highest delay of range */
   125          /* 125/255 of MAX_DELAY msec */
   },

   /*  Param 11: sxedecay[4]. */
   {
   small,
   slider,
   "Decay 4",
   "Decay of Delay 4.",
   0,
   255,         /* 1 */
   100          /* Approx. 100/255 of dynamic range */
   },

   /*  Param 12: sxedelay[4]. */
   {
   small,
   slider,
   "Delay 4",
   "Delay 4 time: 0 to maximum.",
   0,           /* 0 msec. delay  */
   255,         /* Highest delay of range */
   90           /* 90/255 of MAX_DELAY msec */
   },

   /*  Param 13: sxedecay[5]. */
   {
   small,
   slider,
   "Decay 5",
   "Decay of Delay 5.",
   0,
   255,         /* 1 */
   100          /* Approx. 100/255 of dynamic range */
   },

   /*  Param 14: sxedelay[5]. */
   {
   small,
   slider,
   "Delay 5",
   "Delay 5 time: 0 to maximum.",
   0,           /* 0 msec. delay  */
   255,         /* Highest delay of range */
   30           /* 30/255 of MAX_DELAY msec */
   },

   /*  Param 15: sxedecay[6]. */
   {
   small,
   slider,
   "Decay 6",
   "Decay of Delay 6.",
   0,
   255,         /* 1 */
   100          /* Approx. 100/255 of dynamic range */
   },

   /*  Param 16: sxedelay[6]. */
   {
   small,
   slider,
   "Delay 6",
   "Delay 6 time: 0 to maximum.",
   0,           /* 0 msec. delay  */
   255,         /* Highest delay of range */
   5            /* 5/255 of MAX_DELAY msec */
   },
};

/*  State of the soxechos */

typedef struct {
   float   in_gain, out_gain;
   float   sxedecay[MAX_ECHOS];
   float   sxedelay[MAX_ECHOS];
   long    sxesamples[MAX_ECHOS];
   long    echo_cntr[MAX_ECHOS];
   long    echo_ptr[MAX_ECHOS];
   samp*   echo_buf;
   long    maxanysamples;
   int     num_delays;
   double  unit_vol_scale;
   double  exp_fract_vol;
} sxe_state;

int ox_init(machine_type *t) {
   t->long_name = "soxechos (Mueller, Nelson, et al.)";
   t->short_name = "soxechos";
   t->max_tracks = 1;
   t->input_channels = 1;
   t->output_channels = 1;
   t->num_params = 15;
   t->param_specs = sxe_params;

   return 1;
}

void ox_create(machine *m) {
   sxe_state *s;
   int i;
   long echo_band_samples;
//   long j;

   s = (sxe_state*) malloc(sizeof(sxe_state));
   m->state = (void *) s;

   s->num_delays = MAX_ECHOS;

   s->unit_vol_scale = exp((double)DYN_RANGE / 3.0) - 1.0;
   s->exp_fract_vol = ((double)DYN_RANGE / 3) / 255.0;

   /* Set defaults */
   s->in_gain  = ((float)exp(200.0 * s->exp_fract_vol) - 1.0) 
                 / s->unit_vol_scale;
   s->out_gain  = ((float)exp(200.0 * s->exp_fract_vol) - 1.0) 
                 / s->unit_vol_scale;

   s->sxedecay[0] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   s->sxedelay[0] = 255.0/255.0 * (float)MAX_DELAY; 

   s->sxedecay[1] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   s->sxedelay[1] = 210.0/255.0 * (float)MAX_DELAY; 

   s->sxedecay[2] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   s->sxedelay[2] = 170.0/255.0 * (float)MAX_DELAY; 

   s->sxedecay[3] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   s->sxedelay[3] = 125.0/255.0 * (float)MAX_DELAY; 

   s->sxedecay[4] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   s->sxedelay[4] = 90.0/255.0 * (float)MAX_DELAY; 

   s->sxedecay[5] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   s->sxedelay[5] = 30.0/255.0 * (float)MAX_DELAY; 

   s->sxedecay[6] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   s->sxedelay[6] = 5.0/255.0 * (float)MAX_DELAY; 

   echo_band_samples = (long)((float)MAX_DELAY * (float)OX_SAMPLING_RATE / 
1000.0) + 1;

   for(i = 0; i < s->num_delays; i++) {

      s->sxesamples[i] = (long)(s->sxedelay[i] * ((float)OX_SAMPLING_RATE) / 
1000.0 
                                + 0.5);

      s->echo_cntr[i] = 0;
      s->echo_ptr[i] = echo_band_samples * i;
   }

   s->maxanysamples = (long)((float)echo_band_samples * (float)MAX_ECHOS * 
                             (float)OX_SAMPLING_RATE / 1000.0) + 1;

   s->echo_buf = (samp*) malloc(s->maxanysamples * sizeof(samp));

//   memset(s->echo_buf, 0, s->maxanysamples * sizeof(samp)); // Unnecessary

//   for(j = 0; j < s->maxanysamples; j++) { // Speed this up.
//      s->echo_buf[j] = 0.0;
//   }

   return;
}

void ox_destroy(machine *m) {
   free( ((sxe_state*)(m->state)) -> echo_buf);
   free(m->state);
   m->state = NULL;
   return;
}

void ox_update(machine *m) {
   sxe_state *s = (sxe_state *) m->state;
   param temp;
   int i;

   temp = m->params[0][ix_gain_in];
   if (temp != nochange) {
      if      (temp < 0)   temp = 0;
      else if (temp > 255) temp = 255;

      s->in_gain  = ((float)exp((double)temp * s->exp_fract_vol) - 1.0) 
                    / s->unit_vol_scale;
   }

   temp = m->params[0][ix_gain_out];
   if (temp != nochange) {
      if      (temp < 0)   temp = 0;
      else if (temp > 255) temp = 255;

      s->out_gain  = ((float)exp((double)temp * s->exp_fract_vol) - 1.0) 
                     / s->unit_vol_scale;
   }

   for(i = 0; i < s->num_delays; i++) {
      temp = m->params[0][ix_sxedecay + i * 2];
      if (temp != nochange) {
         if      (temp < 0)   temp = 0;
         else if (temp > 255) temp = 255;

         s->sxedecay[i] = ((float)exp((double)temp * s->exp_fract_vol) - 1.0) 
                          / s->unit_vol_scale;
      }

      temp = m->params[0][ix_sxedelay + i * 2];
      if (temp != nochange) {
         if      (temp < 1)   temp = 0;
         else if (temp > 255) temp = 255;

         s->sxedelay[i] = (float)temp/255.0 * (float)MAX_DELAY; 

         s->sxesamples[i] = (long)(s->sxedelay[i] * ((float)OX_SAMPLING_RATE) 
                                   / 1000.0 + 0.5);
         s->echo_cntr[i] %= s->sxesamples[i];
      }

   }

   return;
}

const char *ox_desc(int which_param, param value) {
   static char temp_string[80];
   float user_value;
   int i, percent;

   sprintf(temp_string, "ERROR");

   if (which_param == ix_gain_in || which_param == ix_gain_out) {
      if (value < 0) sprintf(temp_string, "Gain must not be less than 0.");
      else {
         user_value = ((float)value) / 255.0;
         percent = (int)(user_value * 100.0 + 0.5);
         if (user_value > 1.0)
            sprintf(temp_string, "Gain must be not greater than 100%.");

         else sprintf(temp_string, "%3.0f%%", percent);
      }
   }

   else {

      for(i = 0; i < MAX_ECHOS; i++) {
         if (which_param == (ix_sxedecay + i * 2)) {
            if (value < 0) sprintf(temp_string, "%s%d%s", "Decay ", i, 
                           " must not be less than 0.  0 removes this delay");
            else {
               user_value = (float)value / 255.0;
               percent = (int)(user_value * 100.0 + 0.5);
               if (user_value > 1.0)
                  sprintf(temp_string, "%s%d%s", "Decay ", i,
                          "must be not greater than 100%.");

               else sprintf(temp_string, "%3.0f%%", percent);
            }
         }

         else if (which_param == (ix_sxedelay + i * 2)) {
            if (value < 0) sprintf(temp_string, "%s%d%s", "Delay ", i,
                                   " must not be less than 0.");
            else {
               user_value = (float)value / 255.0 * (float)MAX_DELAY;
               if (user_value > (float)MAX_DELAY)
                  sprintf(temp_string, "%s%d%s%d%s", "Delay ", i,
                          " must be not greater than ", MAX_DELAY, " msec.");

               else sprintf(temp_string, "%3.0f", user_value);
            }
         }
      }
   }
}

int ox_work(machine *m, int block_size) {
   sxe_state *s = (sxe_state *) m->state;

   samp *mlout, *mlin, *echobuf;
//   samp *mrout;
   int j;
   long i, numdlys;
   long *sxsmpls, *echoptr, *echoctr;
   float ingain, outgain;
   float *sxdecy;

   /* Speed-up variables */
   mlout    = m->lout;
//   mrout    = m->rout;
   mlin     = m->lin;

   ingain   = s->in_gain;
   outgain  = s->out_gain;

   echobuf   = s->echo_buf;
   echoctr  = (long*)s->echo_cntr;
   echoptr  = (long*)s->echo_ptr;
   sxsmpls  = (long*)s->sxesamples;
   sxdecy   = (float*)s->sxedecay;
   numdlys  = s->num_delays;

   for(i = 0; i < block_size; i++) {

      /* Compute output first */
      mlout[i] = mlin[i] * ingain;

      /* Add echoes */
      for ( j = 0; j < numdlys; j++ ) {
         if (sxdecy[j])
           mlout[i] += echobuf[echoptr[j] + echoctr[j]] * sxdecy[j];
      }

      /* Adjust the output volume */
      mlout[i] *= outgain;

      /* Mix decay of delays and input */
      for ( j = 0; j < numdlys; j++ ) {
         if ( j == 0 )
            echobuf[echoctr[j] + echoptr[j]] = mlin[i];

         else 
            echobuf[echoctr[j] + echoptr[j]] = echobuf[echoctr[j-1] + 
echoptr[j-1]] 
                                               + mlin[i];
      }

      /* Adjust the counters */
      for ( j = 0; j < numdlys; j++ )
         echoctr[j] = ( echoctr[j] + 1 ) % sxsmpls[j];

   }

   return 1;
}

void ox_track(machine *m, int change) {
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]