| 
							- /*
 -   ZynAddSubFX - a software synthesizer
 - 
 -   Unison.cpp - Unison effect (multivoice chorus)
 -   Copyright (C) 2002-2009 Nasca Octavian Paul
 -   Author: Nasca Octavian Paul
 - 
 -   This program is free software; you can redistribute it and/or modify
 -   it under the terms of version 2 of the GNU General Public License
 -   as published by the Free Software Foundation.
 - 
 -   This program is distributed in the hope that it will be useful,
 -   but WITHOUT ANY WARRANTY; without even the implied warranty of
 -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 -   GNU General Public License (version 2 or later) for more details.
 - 
 -   You should have received a copy of the GNU General Public License (version 2)
 -   along with this program; if not, write to the Free Software Foundation,
 -   Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 - */
 - 
 - #include <cmath>
 - #include <cstring>
 - 
 - #include "../Misc/Allocator.h"
 - #include "Unison.h"
 - #include "globals.h"
 - 
 - #ifndef errx
 - #include <err.h>
 - #endif
 - 
 - Unison::Unison(Allocator *alloc_, int update_period_samples_, float max_delay_sec_, float srate_f)
 -     :unison_size(0),
 -       base_freq(1.0f),
 -       uv(NULL),
 -       update_period_samples(update_period_samples_),
 -       update_period_sample_k(0),
 -       max_delay((int)(srate_f * max_delay_sec_) + 1),
 -       delay_k(0),
 -       first_time(false),
 -       delay_buffer(NULL),
 -       unison_amplitude_samples(0.0f),
 -       unison_bandwidth_cents(10.0f),
 -       samplerate_f(srate_f),
 -       alloc(*alloc_)
 - {
 -     if(max_delay < 10)
 -         max_delay = 10;
 -     delay_buffer = alloc.valloc<float>(max_delay);
 -     memset(delay_buffer, 0, max_delay * sizeof(float));
 -     setSize(1);
 - }
 - 
 - Unison::~Unison() {
 -     alloc.devalloc(delay_buffer);
 -     alloc.devalloc(uv);
 - }
 - 
 - void Unison::setSize(int new_size)
 - {
 -     if(new_size < 1)
 -         new_size = 1;
 -     unison_size = new_size;
 -     alloc.devalloc(uv);
 -     uv = alloc.valloc<UnisonVoice>(unison_size);
 -     first_time = true;
 -     updateParameters();
 - }
 - 
 - void Unison::setBaseFrequency(float freq)
 - {
 -     base_freq = freq;
 -     updateParameters();
 - }
 - 
 - void Unison::setBandwidth(float bandwidth)
 - {
 -     if(bandwidth < 0)
 -         bandwidth = 0.0f;
 -     if(bandwidth > 1200.0f)
 -         bandwidth = 1200.0f;
 - 
 -     /* If the bandwidth is too small, the audio may cancel itself out
 -      * (due to the sign change of the outputs)
 -      * TODO figure out the acceptable lower bound and codify it
 -      */
 -     unison_bandwidth_cents = bandwidth;
 -     updateParameters();
 - }
 - 
 - void Unison::updateParameters(void)
 - {
 -     if(!uv)
 -         return;
 -     float increments_per_second = samplerate_f
 -                                   / (float) update_period_samples;
 - //	printf("#%g, %g\n",increments_per_second,base_freq);
 -     for(int i = 0; i < unison_size; ++i) {
 -         float base = powf(UNISON_FREQ_SPAN, SYNTH_T::numRandom() * 2.0f - 1.0f);
 -         uv[i].relative_amplitude = base;
 -         float period = base / base_freq;
 -         float m      = 4.0f / (period * increments_per_second);
 -         if(SYNTH_T::numRandom() < 0.5f)
 -             m = -m;
 -         uv[i].step = m;
 - //		printf("%g %g\n",uv[i].relative_amplitude,period);
 -     }
 - 
 -     float max_speed = powf(2.0f, unison_bandwidth_cents / 1200.0f);
 -     unison_amplitude_samples = 0.125f * (max_speed - 1.0f)
 -                                * samplerate_f / base_freq;
 - 
 -     //If functions exceed this limit, they should have requested a bigguer delay
 -     //and thus are buggy
 -     if(unison_amplitude_samples >= max_delay - 1) {
 -         warnx("BUG: Unison amplitude samples too big");
 -         warnx("Unision max_delay should be larger");
 -         unison_amplitude_samples = max_delay - 2;
 -     }
 - 
 -     updateUnisonData();
 - }
 - 
 - void Unison::process(int bufsize, float *inbuf, float *outbuf)
 - {
 -     if(!uv)
 -         return;
 -     if(!outbuf)
 -         outbuf = inbuf;
 - 
 -     float volume    = 1.0f / sqrtf(unison_size);
 -     float xpos_step = 1.0f / (float) update_period_samples;
 -     float xpos      = (float) update_period_sample_k * xpos_step;
 -     for(int i = 0; i < bufsize; ++i) {
 -         if(update_period_sample_k++ >= update_period_samples) {
 -             updateUnisonData();
 -             update_period_sample_k = 0;
 -             xpos = 0.0f;
 -         }
 -         xpos += xpos_step;
 -         float in   = inbuf[i], out = 0.0f;
 -         float sign = 1.0f;
 -         for(int k = 0; k < unison_size; ++k) {
 -             float vpos = uv[k].realpos1 * (1.0f - xpos) + uv[k].realpos2 * xpos;        //optimize
 -             float pos  = (float)(delay_k + max_delay) - vpos - 1.0f;
 -             int   posi;
 -             F2I(pos, posi); //optimize!
 -             int posi_next = posi + 1;
 -             if(posi >= max_delay)
 -                 posi -= max_delay;
 -             if(posi_next >= max_delay)
 -                 posi_next -= max_delay;
 -             float posf = pos - floorf(pos);
 -             out += ((1.0f - posf) * delay_buffer[posi] + posf
 -                  * delay_buffer[posi_next]) * sign;
 -             sign = -sign;
 -         }
 -         outbuf[i] = out * volume;
 - //		printf("%d %g\n",i,outbuf[i]);
 -         delay_buffer[delay_k] = in;
 -         delay_k = (++delay_k < max_delay) ? delay_k : 0;
 -     }
 - }
 - 
 - void Unison::updateUnisonData()
 - {
 -     if(!uv)
 -         return;
 - 
 -     for(int k = 0; k < unison_size; ++k) {
 -         float pos  = uv[k].position;
 -         float step = uv[k].step;
 -         pos += step;
 -         if(pos <= -1.0f) {
 -             pos  = -1.0f;
 -             step = -step;
 -         }
 -         else
 -         if(pos >= 1.0f) {
 -             pos  = 1.0f;
 -             step = -step;
 -         }
 -         float vibratto_val = (pos - 0.333333333f * pos * pos * pos) * 1.5f; //make the vibratto lfo smoother
 - 
 -         //Relative amplitude is utilized, so the delay may be larger than the
 -         //whole buffer, if the buffer is too small, this indicates a buggy call
 -         //to Unison()
 -         float newval = 1.0f + 0.5f
 -                        * (vibratto_val + 1.0f) * unison_amplitude_samples
 -                        * uv[k].relative_amplitude;
 - 
 -         if(first_time)
 -             uv[k].realpos1 = uv[k].realpos2 = newval;
 -         else {
 -             uv[k].realpos1 = uv[k].realpos2;
 -             uv[k].realpos2 = newval;
 -         }
 - 
 -         uv[k].position = pos;
 -         uv[k].step     = step;
 -     }
 -     first_time = false;
 - }
 
 
  |