1 /*
2   Copyright 2008-2011 David Robillard <http://drobilla.net>
3   Copyright 1999-2000 Paul Kellett (Maxim Digital Audio)
4 
5   This is free software: you can redistribute it and/or modify it
6   under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License,
8   or (at your option) any later version.
9 
10   This software is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13   See the GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "mdaDetune.h"
20 
21 #include <cmath>
22 #include <cstdio>
23 #include <cstring>
24 
createEffectInstance(audioMasterCallback audioMaster)25 AudioEffect *createEffectInstance(audioMasterCallback audioMaster)
26 {
27   return new mdaDetune(audioMaster);
28 }
29 
getProductString(char * text)30 bool  mdaDetune::getProductString(char* text) { strcpy(text, "mda Detune"); return true; }
getVendorString(char * text)31 bool  mdaDetune::getVendorString(char* text)  { strcpy(text, "mda"); return true; }
getEffectName(char * name)32 bool  mdaDetune::getEffectName(char* name)    { strcpy(name, "Detune"); return true; }
33 
mdaDetune(audioMasterCallback audioMaster)34 mdaDetune::mdaDetune(audioMasterCallback audioMaster): AudioEffectX(audioMaster, NPROGS, NPARAMS)
35 {
36   setNumInputs(2);
37   setNumOutputs(2);
38   setUniqueID("mdaDetune");  ///identify mdaDetune-in here
39 	DECLARE_LVZ_DEPRECATED(canMono) ();
40   canProcessReplacing();
41 
42   programs[0].param[0] = 0.20f; //fine
43   programs[0].param[1] = 0.90f; //mix
44   programs[0].param[2] = 0.50f; //output
45   programs[0].param[3] = 0.50f; //chunksize
46   strcpy(programs[0].name, "Stereo Detune");
47   programs[1].param[0] = 0.20f;
48   programs[1].param[1] = 0.90f;
49   programs[1].param[2] = 0.50f;
50   programs[1].param[3] = 0.50f;
51   strcpy(programs[1].name,"Symphonic");
52   programs[2].param[0] = 0.8f;
53   programs[2].param[1] = 0.7f;
54   programs[2].param[2] = 0.50f;
55   programs[2].param[3] = 0.50f;
56   strcpy(programs[2].name,"Out Of Tune");
57 
58   ///initialise...
59   curProgram=0;
60   suspend();
61 
62   semi = 3.0f * 0.20f * 0.20f * 0.20f;
63   dpos2 = (float)pow(1.0594631f, semi);
64   dpos1 = 1.0f / dpos2;
65   wet = 1.0f;
66   dry = wet - wet * 0.90f * 0.90f;
67   wet = (wet + wet - wet * 0.90f) * 0.90f;
68 }
69 
suspend()70 void mdaDetune::suspend() ///clear any buffers...
71 {
72   memset(buf, 0, sizeof (buf));
73   memset(win, 0, sizeof (win));
74   pos0 = 0; pos1 = pos2 = 0.0f;
75 
76   //recalculate crossfade window
77   buflen = 1 << (8 + (int32_t)(4.9f * programs[curProgram].param[3]));
78 	if (buflen > BUFMAX) buflen = BUFMAX;
79   bufres = 1000.0f * (float)buflen / getSampleRate();
80 
81   int32_t i; //hanning half-overlap-and-add
82   double p=0.0, dp=6.28318530718/buflen;
83   for(i=0;i<buflen;i++) { win[i] = (float)(0.5 - 0.5 * cos(p)); p+=dp; }
84 }
85 
86 
setProgram(int32_t program)87 void mdaDetune::setProgram(int32_t program)
88 {
89 	if ((unsigned int)program < NPROGS)
90 	{
91 		curProgram = program;
92 
93 		// update
94 		float * param = programs[curProgram].param;
95     semi = 3.0f * param[0] * param[0] * param[0];
96     dpos2 = (float)pow(1.0594631f, semi);
97     dpos1 = 1.0f / dpos2;
98 
99     wet = (float)pow(10.0f, 2.0f * param[2] - 1.0f);
100     dry = wet - wet * param[1] * param[1];
101     wet = (wet + wet - wet * param[1]) * param[1];
102 
103     int32_t tmp = 1 << (8 + (int32_t)(4.9f * param[3]));
104 
105     if(tmp!=buflen) //recalculate crossfade window
106     {
107       buflen = tmp;
108 	    if (buflen > BUFMAX) buflen = BUFMAX;
109       bufres = 1000.0f * (float)buflen / getSampleRate();
110 
111       int32_t i; //hanning half-overlap-and-add
112       double p=0.0, dp=6.28318530718/buflen;
113       for(i=0;i<buflen;i++) { win[i] = (float)(0.5 - 0.5 * cos(p)); p+=dp; }
114     }
115 	}
116 }
117 
118 
setParameter(int32_t which,float value)119 void  mdaDetune::setParameter(int32_t which, float value)
120 {
121   float * param = programs[curProgram].param;
122   param[which] = value;
123 
124   switch(which)
125   {
126     case  0:
127       semi = 3.0f * param[0] * param[0] * param[0];
128       dpos2 = (float)pow(1.0594631f, semi);
129       dpos1 = 1.0f / dpos2;
130       break;
131     case  1:
132     case  2:
133       wet = (float)pow(10.0f, 2.0f * param[2] - 1.0f);
134       dry = wet - wet * param[1] * param[1];
135       wet = (wet + wet - wet * param[1]) * param[1];
136       break;
137     case 3:
138     {
139       int32_t tmp = 1 << (8 + (int32_t)(4.9f * param[3]));
140 
141       if(tmp!=buflen) //recalculate crossfade window
142       {
143         buflen = tmp;
144 	      if (buflen > BUFMAX) buflen = BUFMAX;
145         bufres = 1000.0f * (float)buflen / getSampleRate();
146 
147         int32_t i; //hanning half-overlap-and-add
148         double p=0.0, dp=6.28318530718/buflen;
149         for(i=0;i<buflen;i++) { win[i] = (float)(0.5 - 0.5 * cos(p)); p+=dp; }
150       }
151       break;
152     }
153     default:
154       break;
155   }
156 }
157 
158 
getParameter(int32_t which)159 float mdaDetune::getParameter(int32_t which) { return programs[curProgram].param[which]; }
setProgramName(char * name)160 void  mdaDetune::setProgramName(char *name) { strcpy(programs[curProgram].name, name); }
getProgramName(char * name)161 void  mdaDetune::getProgramName(char *name) { strcpy(name, programs[curProgram].name); }
getProgramNameIndexed(int32_t category,int32_t which,char * name)162 bool mdaDetune::getProgramNameIndexed (int32_t category, int32_t which, char* name)
163 {
164 	if ((unsigned int)which < NPROGS)
165 	{
166 	    strcpy(name, programs[which].name);
167 	    return true;
168 	}
169 	return false;
170 }
171 
172 
getParameterName(int32_t which,char * label)173 void mdaDetune::getParameterName(int32_t which, char *label)
174 {
175   switch(which)
176   {
177     case  0: strcpy(label, "Detune"); break;
178     case  1: strcpy(label, "Mix"); break;
179     case  2: strcpy(label, "Output"); break;
180     default: strcpy(label, "Latency");
181   }
182 }
183 
184 
getParameterDisplay(int32_t which,char * text)185 void mdaDetune::getParameterDisplay(int32_t which, char *text)
186 {
187  	char string[16];
188 
189   switch(which)
190   {
191     case  1: sprintf(string, "%.0f", 99.0f * programs[curProgram].param[which]); break;
192     case  2: sprintf(string, "%.1f", 40.0f * programs[curProgram].param[which] - 20.0f); break;
193     case  3: sprintf(string, "%.1f", bufres); break;
194     default: sprintf(string, "%.1f", 100.0f * semi);
195   }
196 	string[8] = 0;
197 	strcpy(text, (char *)string);
198 }
199 
200 
getParameterLabel(int32_t which,char * label)201 void mdaDetune::getParameterLabel(int32_t which, char *label)
202 {
203   switch(which)
204   {
205     case  0: strcpy(label, "cents"); break;
206     case  1: strcpy(label, "%"); break;
207     case  2: strcpy(label, "dB"); break;
208     default: strcpy(label, "ms");
209   }
210 }
211 
212 
process(float ** inputs,float ** outputs,int32_t sampleFrames)213 void mdaDetune::process(float **inputs, float **outputs, int32_t sampleFrames)
214 {
215   float *in1 = inputs[0];
216   float *in2 = inputs[1];
217   float *out1 = outputs[0];
218   float *out2 = outputs[1];
219   float a, b, c, d;
220   float x, w=wet, y=dry, p1=pos1, p1f, d1=dpos1;
221   float                  p2=pos2,      d2=dpos2;
222   int32_t  p0=pos0, p1i, p2i;
223   int32_t  l=buflen-1, lh=buflen>>1;
224   float lf = (float)buflen;
225 
226   --in1;
227   --in2;
228   --out1;
229   --out2;
230   while(--sampleFrames >= 0)
231   {
232     a = *++in1;
233     b = *++in2;
234     c = out1[1];
235     d = out2[1];
236 
237     c += y * a;
238     d += y * b;
239 
240     --p0 &= l;
241     *(buf + p0) = w * (a + b);      //input
242 
243     p1 -= d1;
244     if(p1<0.0f) p1 += lf;           //output
245     p1i = (int32_t)p1;
246     p1f = p1 - (float)p1i;
247     a = *(buf + p1i);
248     ++p1i &= l;
249     a += p1f * (*(buf + p1i) - a);  //linear interpolation
250 
251     p2i = (p1i + lh) & l;           //180-degree ouptut
252     b = *(buf + p2i);
253     ++p2i &= l;
254     b += p1f * (*(buf + p2i) - b);  //linear interpolation
255 
256     p2i = (p1i - p0) & l;           //crossfade window
257     x = *(win + p2i);
258     //++p2i &= l;
259     //x += p1f * (*(win + p2i) - x); //linear interpolation (doesn't do much)
260     c += b + x * (a - b);
261 
262     p2 -= d2;                //repeat for downwards shift - can't see a more efficient way?
263     if(p2<0.0f) p2 += lf;           //output
264     p1i = (int32_t)p2;
265     p1f = p2 - (float)p1i;
266     a = *(buf + p1i);
267     ++p1i &= l;
268     a += p1f * (*(buf + p1i) - a);  //linear interpolation
269 
270     p2i = (p1i + lh) & l;           //180-degree ouptut
271     b = *(buf + p2i);
272     ++p2i &= l;
273     b += p1f * (*(buf + p2i) - b);  //linear interpolation
274 
275     p2i = (p1i - p0) & l;           //crossfade window
276     x = *(win + p2i);
277     //++p2i &= l;
278     //x += p1f * (*(win + p2i) - x); //linear interpolation (doesn't do much)
279     d += b + x * (a - b);
280 
281     *++out1 = c;
282     *++out2 = d;
283   }
284   pos0=p0; pos1=p1; pos2=p2;
285 }
286 
287 
processReplacing(float ** inputs,float ** outputs,int32_t sampleFrames)288 void mdaDetune::processReplacing(float **inputs, float **outputs, int32_t sampleFrames)
289 {
290   float *in1 = inputs[0];
291   float *in2 = inputs[1];
292   float *out1 = outputs[0];
293   float *out2 = outputs[1];
294   float a, b, c, d;
295   float x, w=wet, y=dry, p1=pos1, p1f, d1=dpos1;
296   float                  p2=pos2,      d2=dpos2;
297   int32_t  p0=pos0, p1i, p2i;
298   int32_t  l=buflen-1, lh=buflen>>1;
299   float lf = (float)buflen;
300 
301   --in1;
302   --in2;
303   --out1;
304   --out2;
305   while(--sampleFrames >= 0)  //had to disable optimization /Og in MSVC++5!
306   {
307     a = *++in1;
308     b = *++in2;
309 
310     c = y * a;
311     d = y * b;
312 
313     --p0 &= l;
314     *(buf + p0) = w * (a + b);      //input
315 
316     p1 -= d1;
317     if(p1<0.0f) p1 += lf;           //output
318     p1i = (int32_t)p1;
319     p1f = p1 - (float)p1i;
320     a = *(buf + p1i);
321     ++p1i &= l;
322     a += p1f * (*(buf + p1i) - a);  //linear interpolation
323 
324     p2i = (p1i + lh) & l;           //180-degree ouptut
325     b = *(buf + p2i);
326     ++p2i &= l;
327     b += p1f * (*(buf + p2i) - b);  //linear interpolation
328 
329     p2i = (p1i - p0) & l;           //crossfade
330     x = *(win + p2i);
331     //++p2i &= l;
332     //x += p1f * (*(win + p2i) - x); //linear interpolation (doesn't do much)
333     c += b + x * (a - b);
334 
335     p2 -= d2;  //repeat for downwards shift - can't see a more efficient way?
336     if(p2<0.0f) p2 += lf;           //output
337     p1i = (int32_t)p2;
338     p1f = p2 - (float)p1i;
339     a = *(buf + p1i);
340     ++p1i &= l;
341     a += p1f * (*(buf + p1i) - a);  //linear interpolation
342 
343     p2i = (p1i + lh) & l;           //180-degree ouptut
344     b = *(buf + p2i);
345     ++p2i &= l;
346     b += p1f * (*(buf + p2i) - b);  //linear interpolation
347 
348     p2i = (p1i - p0) & l;           //crossfade
349     x = *(win + p2i);
350     //++p2i &= l;
351     //x += p1f * (*(win + p2i) - x); //linear interpolation (doesn't do much)
352     d += b + x * (a - b);
353 
354     *++out1 = c;
355     *++out2 = d;
356   }
357   pos0=p0; pos1=p1; pos2=p2;
358 }
359