[Top][All Lists]
[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) {
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Octal-dev] Corrected copy of soxchorus.c.,
Neil Nelson <=