1 /* Effect: phaser Copyright (C) 1998 Juergen Mueller And Sundry Contributors
2 *
3 * This source code is freely redistributable and may be used for
4 * any purpose. This copyright notice must be maintained.
5 * Juergen Mueller And Sundry Contributors are not responsible for
6 * the consequences of using this software.
7 *
8 * Flow diagram scheme: August 24, 1998
9 *
10 * * gain-in +---+ * gain-out
11 * ibuff ----------->| |----------------------------------> obuff
12 * | + | * decay
13 * | |<------------+
14 * +---+ _______ |
15 * | | | |
16 * +---| delay |---+
17 * |_______|
18 * /|\
19 * |
20 * +---------------+ +------------------+
21 * | Delay control |<-----| modulation speed |
22 * +---------------+ +------------------+
23 *
24 * The delay is controled by a sine or triangle modulation.
25 *
26 * Usage:
27 * phaser gain-in gain-out delay decay speed [ -s | -t ]
28 *
29 * Where:
30 * gain-in, decay : 0.0 .. 1.0 volume
31 * gain-out : 0.0 .. volume
32 * delay : 0.0 .. 5.0 msec
33 * speed : 0.1 .. 2.0 Hz modulation speed
34 * -s : modulation by sine (default)
35 * -t : modulation by triangle
36 *
37 * Note:
38 * When decay is close to 1.0, the samples may begin clipping or the output
39 * can saturate! Hint:
40 * in-gain < (1 - decay * decay)
41 * 1 / out-gain > gain-in / (1 - decay)
42 */
43
44 #include "sox_i.h"
45 #include <string.h>
46
47 typedef struct {
48 double in_gain, out_gain, delay_ms, decay, mod_speed;
49 lsx_wave_t mod_type;
50
51 int * mod_buf;
52 size_t mod_buf_len;
53 int mod_pos;
54
55 double * delay_buf;
56 size_t delay_buf_len;
57 int delay_pos;
58 } priv_t;
59
getopts(sox_effect_t * effp,int argc,char ** argv)60 static int getopts(sox_effect_t * effp, int argc, char * * argv)
61 {
62 priv_t * p = (priv_t *) effp->priv;
63 char chars[2];
64
65 /* Set non-zero defaults: */
66 p->in_gain = .4;
67 p->out_gain = .74;
68 p->delay_ms = 3.;
69 p->decay = .4;
70 p->mod_speed = .5;
71
72 --argc, ++argv;
73 do { /* break-able block */
74 NUMERIC_PARAMETER(in_gain , .0, 1)
75 NUMERIC_PARAMETER(out_gain , .0, 1e9)
76 NUMERIC_PARAMETER(delay_ms , .0, 5)
77 NUMERIC_PARAMETER(decay , .0, .99)
78 NUMERIC_PARAMETER(mod_speed, .1, 2)
79 } while (0);
80
81 if (argc && sscanf(*argv, "-%1[st]%c", chars, chars + 1) == 1) {
82 p->mod_type = *chars == 's'? SOX_WAVE_SINE : SOX_WAVE_TRIANGLE;
83 --argc, ++argv;
84 }
85
86 if (p->in_gain > (1 - p->decay * p->decay))
87 lsx_warn("warning: gain-in might cause clipping");
88 if (p->in_gain / (1 - p->decay) > 1 / p->out_gain)
89 lsx_warn("warning: gain-out might cause clipping");
90
91 return argc? lsx_usage(effp) : SOX_SUCCESS;
92 }
93
start(sox_effect_t * effp)94 static int start(sox_effect_t * effp)
95 {
96 priv_t * p = (priv_t *) effp->priv;
97
98 p->delay_buf_len = p->delay_ms * .001 * effp->in_signal.rate + .5;
99 p->delay_buf = lsx_calloc(p->delay_buf_len, sizeof(*p->delay_buf));
100
101 p->mod_buf_len = effp->in_signal.rate / p->mod_speed + .5;
102 p->mod_buf = lsx_malloc(p->mod_buf_len * sizeof(*p->mod_buf));
103 lsx_generate_wave_table(p->mod_type, SOX_INT, p->mod_buf, p->mod_buf_len,
104 1., (double)p->delay_buf_len, M_PI_2);
105
106 p->delay_pos = p->mod_pos = 0;
107
108 effp->out_signal.length = SOX_UNKNOWN_LEN; /* TODO: calculate actual length */
109 return SOX_SUCCESS;
110 }
111
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)112 static int flow(sox_effect_t * effp, const sox_sample_t *ibuf,
113 sox_sample_t *obuf, size_t *isamp, size_t *osamp)
114 {
115 priv_t * p = (priv_t *) effp->priv;
116 size_t len = *isamp = *osamp = min(*isamp, *osamp);
117
118 while (len--) {
119 double d = *ibuf++ * p->in_gain + p->delay_buf[
120 (p->delay_pos + p->mod_buf[p->mod_pos]) % p->delay_buf_len] * p->decay;
121 p->mod_pos = (p->mod_pos + 1) % p->mod_buf_len;
122
123 p->delay_pos = (p->delay_pos + 1) % p->delay_buf_len;
124 p->delay_buf[p->delay_pos] = d;
125
126 *obuf++ = SOX_ROUND_CLIP_COUNT(d * p->out_gain, effp->clips);
127 }
128 return SOX_SUCCESS;
129 }
130
stop(sox_effect_t * effp)131 static int stop(sox_effect_t * effp)
132 {
133 priv_t * p = (priv_t *) effp->priv;
134
135 free(p->delay_buf);
136 free(p->mod_buf);
137 return SOX_SUCCESS;
138 }
139
lsx_phaser_effect_fn(void)140 sox_effect_handler_t const * lsx_phaser_effect_fn(void)
141 {
142 static sox_effect_handler_t handler = {
143 "phaser", "gain-in gain-out delay decay speed [ -s | -t ]",
144 SOX_EFF_LENGTH | SOX_EFF_GAIN, getopts, start, flow, NULL, stop, NULL, sizeof(priv_t)
145 };
146 return &handler;
147 }
148