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