octal-dev
[Top][All Lists]
Advanced

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

[Octal-dev] Corrected copy of soxchorus.c.


From: Neil Nelson
Subject: [Octal-dev] Corrected copy of soxchorus.c.
Date: Wed, 28 Jun 2000 15:23:35 -0700

The clicks are gone (I think).  It helps to use long index pointers
instead of int.

Neil Nelson
/*   SOXCHORUS.C
*
* This code has been adapted from the SoX chorus.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 26, 2000.
*
* This software is distributed under the terms of the
* GNU General Public License (GPL). Read the included file
* COPYING for more information.
*
*
*       Chorus effect.
* 
* Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ):
*
*        * gain-in                                           ___
* ibuff -----+--------------------------------------------->|   |
*            |      _________                               |   |
*            |     |         |                   * decay 1  |   |
*            +---->| delay 1 |----------------------------->|   |
*            |     |_________|                              |   |
*            |        /|\                                   |   |
*            :         |                                    |   |
*            : +-----------------+   +--------------+       | + |
*            : | Delay control 1 |<--| mod. speed 1 |       |   |
*            : +-----------------+   +--------------+       |   |
*            |      _________                               |   |
*            |     |         |                   * decay n  |   |
*            +---->| delay n |----------------------------->|   |
*                  |_________|                              |   |
*                     /|\                                   |___|
*                      |                                      |  
*              +-----------------+   +--------------+         | * gain-out
*              | Delay control n |<--| mod. speed n |         |
*              +-----------------+   +--------------+         +----->obuff
*
*
* The delay i is controled by a sine or triangle modulation i ( 1 <= i <= n).
*
* Usage: // SoX command line format.
*   chorus gain-in gain-out delay-1 decay-1 speed-1 depth-1 -s1|t1 [
*       delay-2 decay-2 speed-2 depth-2 -s2|-t2 ... ]
*
* Where:
*   gain-in, decay-1 ... decay-n :  0.0 ... 1.0      volume
*   gain-out :  0.0 ...      volume
*   delay-1 ... delay-n :  20.0 ... 100.0 msec
*   speed-1 ... speed-n :  0.1 ... 5.0 Hz       modulation 1 ... n
*   depth-1 ... depth-n :  0.0 ... 10.0 msec    modulated delay 1 ... n
*   -s1 ... -sn : modulation by sine 1 ... n
*   -t1 ... -tn : modulation by triangle 1 ... n
*
* 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 )
*
* Potential Improvements:
*  (1) Except for the expected fixed number of sxc_params, this program 
*      could handle an arbitrary number of chorus voices.  What trouble
*      would it be to provide for an arbitrary number of sxc_params?
*
*/

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

#define PI2 (6.28318530717958465692)
#define MOD_SINE 0
#define MOD_TRIANGLE 1
#define MOD_SILENT 2

#define DYN_RANGE 10
#define MAX_CHORUS 3

/*  The parameters are:  */
enum {
   ix_gain_in, ix_gain_out,
   ix_sxcdecay, ix_sxcdelay, ix_speed, ix_depth, ix_mod_type,
   ix_sxcdecay_1, ix_sxcdelay_1, ix_speed_1, ix_depth_1, ix_mod_type_1,
   ix_sxcdecay_2, ix_sxcdelay_2, ix_speed_2, ix_depth_2, ix_mod_type_2
} param_index;

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

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

   /*  Param 3: sxcdecay[0]. */
   {
   small,
   slider,
   "Decay 0",
   "Decay of 0th chorus voice.",
   0,
   255,         /* 1 */
   240         /* Approx. 240/255 of dynamic range */
   },

   /*  Param 4: sxcdelay[0]. */
   {
   small,
   slider,
   "Delay 0",
   "Delay: 20 to 100 msec., for 0th voice.",
   0,           /* Minimum 20 msec. delay  */
   255,         /* Maximum 100 msec. delay */
   255
   },

   /*  Param 5: speed[0]. */
   {
   small,
   slider,
   "Speed 0",
   "Modulation Speed: 0.1 to 5 Hz., for 0th voice.",
   0,           /* 0.1 Hz */
   255,         /* 5 Hz */
   20           /* 20/255 * 5 Hz */
   },

   /*  Param 6: depth[0]. */
   {
   small,
   slider,
   "Depth 0",
   "Depth Modulation Delay: 0 to 10 msec., for 0th voice.",
   0,           /* 0 msec. depth  */
   255,         /* Maximum 10 msec. depth */
   255
   },

   /*  Param 7: mod_type[0]. */
   {
   small,
   slider,
   "Modulation 0"
   "Modulation Type: 0=sine (default), 1=triangle, 2=voice off, for 0th voice.",
   0,           /* Sine  */
   2,           /* Voice off  */
   1            /* Triangle */
   },

   /*  Param 8: sxcdecay[1]). */
   {
   small,
   slider,
   "Decay 1",
   "Decay of 1st chorus voice.",
   0,
   255,         /* 1 */
   150         /* Approx. 150/255 of dynamic range */
   },

   /*  Param 9: sxcdelay[1]. */
   {
   small,
   slider,
   "Delay 1",
   "Delay: 20 to 100 msec., for 1st voice.",
   0,           /* Minimum 20 msec. delay  */
   255,         /* Maximum 100 msec. delay */
   255
   },

   /*  Param 10: speed[1]. */
   {
   small,
   slider,
   "Speed 1",
   "Modulation Speed: 0.1 to 5 Hz., for 1st voice.",
   0,           /* 0.1 Hz */
   255,         /* 5 Hz */
   50           /* 50/255 * 5 Hz */
   },

   /*  Param 11: depth[1]. */
   {
   small,
   slider,
   "Depth 1",
   "Depth Modulation  Delay: 0 to 10 msec., for 1st voice.",
   0,           /* 0 msec. depth  */
   255,         /* Maximum 10 msec. depth */
   255
   },

   /*  Param 12: mod_type[1]. */
   {
   small,
   slider,
   "Modulation 1"
   "Modulation Type: 0=sine (default), 1=triangle, 2=voice off, for 1st voice.",
   0,           /* Sine  */
   2,           /* Voice off  */
   0            /* Sine */
   },

   /*  Param 13: sxcdecay[2]. */
   {
   small,
   slider,
   "Decay 2",
   "Decay of 2nd chorus voice.",
   0,
   255,         /* 1 */
   100         /* Approx. 100/255 of dynamic range */
   },

   /*  Param 14: sxcdelay[2]. */
   {
   small,
   slider,
   "Delay 2",
   "Delay: 20 to 100 msec., for 2nd voice.",
   0,           /* Minimum 20 msec. delay  */
   255,         /* Maximum 100 msec. delay */
   255
   },

   /*  Param 15: speed[2]. */
   {
   small,
   slider,
   "Speed 2",
   "Modulation Speed: 0.1 to 5 Hz., for 2nd voice.",
   0,           /* 0.1 Hz */
   255,         /* 5 Hz */
   255          /* 255/255 * 5 Hz */

   },

   /*  Param 16: depth[2]. */
   {
   small,
   slider,
   "Depth 2",
   "Depth Modulation Delay: 0 to 10 msec., for 2nd voice.",
   0,           /* 0 msec. depth  */
   255,         /* Maximum 10 msec. depth */
   255
   },

   /*  Param 17: mod_type[2]. */
   {
   small,
   slider,
   "Modulation 2"
   "Modulation Type: 0=sine (default), 1=triangle, 2=voice off, for 2nd voice.",
   0,           /* Sine  */
   2,           /* Voice off  */
   0            /* Sine */
   },
};

/*  State of the soxchorus. */

typedef struct {
   float   in_gain, out_gain;
   float   sxcdecay[MAX_CHORUS];
   float   sxcdelay[MAX_CHORUS];
   float   speed[MAX_CHORUS];
   float   depth[MAX_CHORUS];
   int     mod_type[MAX_CHORUS];
   long    depth_smpls[MAX_CHORUS];
   long    ttlchrssmpls[MAX_CHORUS];
   long    offset[MAX_CHORUS];
   long    phase[MAX_CHORUS];
   long    phz_width[MAX_CHORUS];
   samp*   chorusbuf;
   float   maxchrsdelay;
   long    maxttlchrssmpls;
   long    maxchrssmpls;
   long    chorus_cntr;
   int     chorus_voices;
   double  unit_vol_scale;
   double  exp_fract_vol;
//   float   clip; // Put in after clipping issue decided.
} sxc_state;

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

   return 1;
}

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

   s = (sxc_state*) malloc(sizeof(sxc_state));

   s->chorus_voices = MAX_CHORUS;

   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(220.0 * s->exp_fract_vol) - 1.0)
                 / s->unit_vol_scale;
   s->out_gain  = ((float)exp(230.0 * s->exp_fract_vol) - 1.0)
                 / s->unit_vol_scale;

   s->maxttlchrssmpls = 0;

   s->sxcdecay[0] = ((float)exp(240.0 * s->exp_fract_vol) - 1.0)
                    / s->unit_vol_scale;
   s->sxcdelay[0] = 255.0/255.0 * 80.0 + 20.0;
   s->speed[0]    = 20.0/255.0 * 4.9 + 0.1;
   s->depth[0]    = 255.0/255.0 * 10.0;
   s->mod_type[0] = 1;

   s->sxcdecay[1] = ((float)exp(150.0 * s->exp_fract_vol) - 1.0)
                    / s->unit_vol_scale;
   s->sxcdelay[1] = 255/255.0 * 80.0 + 20.0;
   s->speed[1]    = 50.0/255.0 * 4.9 + 0.1;        
   s->depth[0]    = 255.0/255.0 * 10.0;
   s->mod_type[1] = 0;                        

   s->sxcdecay[2] = ((float)exp(100.0 * s->exp_fract_vol) - 1.0)
                    / s->unit_vol_scale;
   s->sxcdelay[2] = 255.0/255.0 * 80.0 + 20.0;
   s->speed[2]    = 255.0/255.0 * 4.9 + 0.1;
   s->depth[2]    = 255.0/255.0 * 10.0;
   s->mod_type[2] = 0;

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

      s->depth_smpls[i] = (long)(s->depth[i] * ((float)OX_SAMPLING_RATE) / 
1000.0
                                 + 0.5);
      s->ttlchrssmpls[i] = (long)((s->sxcdelay[i] + s->depth[i])
                         * ((float)OX_SAMPLING_RATE) / 1000.0 + 0.5);

      if (s->ttlchrssmpls[i] > s->maxttlchrssmpls)
         s->maxttlchrssmpls = s->ttlchrssmpls[i];

      if (s->mod_type[i] == 0)
         s->offset[i] = (long)((s->ttlchrssmpls[i] -1) - s->depth_smpls[i]);
      else if (s->mod_type[i] == 1)
         s->offset[i] = (long)((s->ttlchrssmpls[i] -1) - 2 * s->depth_smpls[i]);
      else // if (s->mod_type[i] == 2)
         s->offset[i] = 0;

      s->phz_width[i] = (long)(((float)OX_SAMPLING_RATE) / s->speed[i] + 0.5);
      s->phase[i] = 0;
   }

   s->maxchrsdelay = 110.0;  /* Sum of maximum delay (100) and depth (10). */
   s->maxchrssmpls = (long)(s->maxchrsdelay * ((float)OX_SAMPLING_RATE) 
                     / 1000.0 + 0.5);

   s->chorus_cntr = 0; 

//   s->clip = ?; // Put in after clipping issue decided.

   s->chorusbuf = (samp*) malloc(s->maxchrssmpls * sizeof(samp));

//   for(j = 0; j < s->maxchrssmpls; j++) {
//      s->chorusbuf[j] = 0;
//   }

   m->state = (void *) s;
   return;
}

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

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

   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;
   }

   s->maxttlchrssmpls = 0;

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

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

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

         s->sxcdelay[i] = ((float)temp) / 255.0 * 80.0 + 20.0;;
         s->ttlchrssmpls[i] = (long)(s->sxcdelay[i] * ((float)OX_SAMPLING_RATE) 
                               / 1000.0 + 0.5);
      }

      temp = m->params[0][ix_speed + i * 5];
      if (temp != nochange) {
         if      (temp < 10)  temp = 10;
         else if (temp > 255) temp = 255;

         s->speed[i] = ((float)temp) / 255.0 * 4.9 + 0.1;
         s->phz_width[i] = (long)(((float)OX_SAMPLING_RATE) / s->speed[i] + 
0.5);
      }

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

         s->depth[i] = ((float)temp) / 255.0 * 10.0;
         s->depth_smpls[i] = (long)(s->depth[i] * ((float)OX_SAMPLING_RATE) / 
1000.0 
                                    + 0.5);
      }

      temp = m->params[0][ix_mod_type + i * 5];
      if (temp != nochange) {
         if (temp > 2 || temp < 0) temp = 2; // If out-of-range, turn off voice.
         s->mod_type[i] = temp;
      }

      s->ttlchrssmpls[i] = (long)((s->sxcdelay[i] + s->depth[i])
                         * ((float)OX_SAMPLING_RATE) / 1000.0 + 0.5);

      if (s->ttlchrssmpls[i] > s->maxttlchrssmpls)
         s->maxttlchrssmpls = s->ttlchrssmpls[i];

      if (s->mod_type[i] == 0)
         s->offset[i] = (long)((s->ttlchrssmpls[i] - 1) - s->depth_smpls[i]);
      else if (s->mod_type[i] == 1)
         s->offset[i] = (long)((s->ttlchrssmpls[i] - 1) - 2 * 
s->depth_smpls[i]);
      else // if (s->mod_type[i] == 2)
         s->offset[i] = 0;

   }

   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_CHORUS; i++) {
         if (which_param == (ix_sxcdecay + i * 5)) {
            if (value < 0) sprintf(temp_string, "%s%d%s", "Decay ", i, 
                                   " 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, "%s%d%s", "Decay ", i,
                          "must be not greater than 100%.");

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

         else if (which_param == (ix_sxcdelay + i * 5)) {
            if (value < 0) sprintf(temp_string, "%s%d%s", "Delay ", i,
                                   " must not be less than 0 (20 msec.");
            else {
               user_value = (float)value / 255.0 * 80.0 + 20.0;
               if (user_value > 100.0)
                  sprintf(temp_string, "%s%d%s", "Delay ", i,
                          " must be not greater than 100 msec.");

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

         else if (which_param == ix_speed + i * 5) {
            if (value < 0) sprintf(temp_string, "%s%d%s", "Speed ", i,
                                   " must not be less than 0 (0.1 Hz).");
            else {
               user_value = ((float) value) / 255.0 * 5.0;
               if (user_value > 5.0)
                  sprintf(temp_string, "%s%d%s", "Speed ", i,
                          " must be not greater than 5 Hz.");

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

         else if (which_param == ix_depth + i * 5) {
            if (value < 0) sprintf(temp_string, "%s%d%s", "Depth ", i,
                                   " must not be less than 0 msec.");
            else {
               user_value = ((float) value) / 255.0 * 10.0;
               if (user_value > 10.0)
                  sprintf(temp_string, "%s%d%s", "Depth ", i,
                          " must be not greater than 10 msec.");

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

         else if (which_param == ix_mod_type + i * 5) {
            switch (value) {
               case 0  : sprintf(temp_string, "Sine"); break;
               case 1  : sprintf(temp_string, "Triangle"); break;
               default : sprintf(temp_string, "%s%d%s", "Error: Modulation", i,
                           "type must be 0 = sine, 1 = triangle, or 2 = voice 
off.");
            }
         }
      }
   }

/* SoX: Clipping - Be nice and check the hint with warning, if... 
   if ( flanger->in_gain * ( 1.0 + flanger->sxcdecay ) > 1.0 / 
flanger->out_gain )
      warn("flanger: warning >>> gain-out can cause saturation or clipping of 
output <<<");
*/

}

int ox_work(machine *m, int block_size) {
   sxc_state *s = (sxc_state *) m->state;
   int j, chrsvces;
   int *modtype;
   long i, x_phz, mxttlsmples, chrscntr;
   long *phaz, *offst, *phzwidth, *dpthsmpls;
   float x_flt, ingain, outgain; 
   float* dcy;

   samp *mlout, *mlin, *chrsbuf;
//   samp* mrout;

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

   ingain   = s->in_gain;
   outgain  = s->out_gain;
   chrsbuf  = s->chorusbuf;
   chrsvces = s->chorus_voices;
   modtype  = (int*)s->mod_type;
   phaz     = (long*)s->phase;
   phzwidth = (long*)s->phz_width;
   offst    = (long*)s->offset;
   dpthsmpls = (long*)s->depth_smpls;
   mxttlsmples = s->maxttlchrssmpls;
   chrscntr = s->chorus_cntr;
   dcy      = s->sxcdecay;

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

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

      for ( j = 0; j < chrsvces; j++ ) {

         if (modtype[j] != MOD_SILENT) {
            if (modtype[j] == MOD_SINE) {

               // Get sine phase shift. 1 added to correct noise at 0.
               x_flt = (float) sin((double)phaz[j]/(double)phzwidth[j] * PI2);
               x_phz = offst[j] + (int) (x_flt * (double)dpthsmpls[j] + 0.5);
            }

            else { // modtype[j] == MOD_TRIANGLE

               // Compute triangle - ramp up and down.
               if (phaz[j] < (s->phz_width[j] / 2)) { // Ramp up
                  x_flt = (float)phaz[j] * 2.0 / (float)phzwidth[j];
                  x_phz = offst[j] + (int) (x_flt * 2.0 * (float)dpthsmpls[j] + 
0.5);
               }

               else { // Ramp down
                  x_flt = (float)(phzwidth[j] - phaz[j]) * 2.0 / 
(float)phzwidth[j];
                  x_phz = offst[j] + (int) (x_flt * 2.0 * (float)dpthsmpls[j] + 
0.5);
               }
            }

         mlout[i] += chrsbuf[(mxttlsmples + chrscntr - x_phz) % mxttlsmples] * 
dcy[j];

         }
      }

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

      /* Check for clipping.  Not implemented. */
//      if      (mlout[i] > hig_clip)  mlout[i] = hig_clip;
//      else if (mlout[i] < low_clip)  mlout[i] = low_clip;

      /* Mix decay of delay and input */
      chrsbuf[chrscntr] = mlin[i];

      chrscntr = ( chrscntr + 1 ) % mxttlsmples;

      for ( j = 0; j < s->chorus_voices; j++ )
         phaz[j] = ( phaz[j] + 1 ) % phzwidth[j];
   }

   /* Save speed-up variables to state. */
   s->chorus_cntr = chrscntr;

   return 1;
}


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

reply via email to

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