Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Unison.cpp 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. Unison.cpp - Unison effect (multivoice chorus)
  4. Copyright (C) 2002-2009 Nasca Octavian Paul
  5. Author: Nasca Octavian Paul
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10. */
  11. #include <cmath>
  12. #include <cstring>
  13. #include "../Misc/Allocator.h"
  14. #include "Unison.h"
  15. #include "globals.h"
  16. #define errx(...) {}
  17. #define warnx(...) {}
  18. #ifndef errx
  19. #include <err.h>
  20. #endif
  21. namespace zyncarla {
  22. Unison::Unison(Allocator *alloc_, int update_period_samples_, float max_delay_sec_, float srate_f)
  23. :unison_size(0),
  24. base_freq(1.0f),
  25. uv(NULL),
  26. update_period_samples(update_period_samples_),
  27. update_period_sample_k(0),
  28. max_delay((int)(srate_f * max_delay_sec_) + 1),
  29. delay_k(0),
  30. first_time(false),
  31. delay_buffer(NULL),
  32. unison_amplitude_samples(0.0f),
  33. unison_bandwidth_cents(10.0f),
  34. samplerate_f(srate_f),
  35. alloc(*alloc_)
  36. {
  37. if(max_delay < 10)
  38. max_delay = 10;
  39. delay_buffer = alloc.valloc<float>(max_delay);
  40. memset(delay_buffer, 0, max_delay * sizeof(float));
  41. setSize(1);
  42. }
  43. Unison::~Unison() {
  44. alloc.devalloc(delay_buffer);
  45. alloc.devalloc(uv);
  46. }
  47. void Unison::setSize(int new_size)
  48. {
  49. if(new_size < 1)
  50. new_size = 1;
  51. unison_size = new_size;
  52. alloc.devalloc(uv);
  53. uv = alloc.valloc<UnisonVoice>(unison_size);
  54. first_time = true;
  55. updateParameters();
  56. }
  57. void Unison::setBaseFrequency(float freq)
  58. {
  59. base_freq = freq;
  60. updateParameters();
  61. }
  62. void Unison::setBandwidth(float bandwidth)
  63. {
  64. if(bandwidth < 0)
  65. bandwidth = 0.0f;
  66. if(bandwidth > 1200.0f)
  67. bandwidth = 1200.0f;
  68. /* If the bandwidth is too small, the audio may cancel itself out
  69. * (due to the sign change of the outputs)
  70. * TODO figure out the acceptable lower bound and codify it
  71. */
  72. unison_bandwidth_cents = bandwidth;
  73. updateParameters();
  74. }
  75. void Unison::updateParameters(void)
  76. {
  77. if(!uv)
  78. return;
  79. float increments_per_second = samplerate_f
  80. / (float) update_period_samples;
  81. // printf("#%g, %g\n",increments_per_second,base_freq);
  82. for(int i = 0; i < unison_size; ++i) {
  83. float base = powf(UNISON_FREQ_SPAN, SYNTH_T::numRandom() * 2.0f - 1.0f);
  84. uv[i].relative_amplitude = base;
  85. float period = base / base_freq;
  86. float m = 4.0f / (period * increments_per_second);
  87. if(SYNTH_T::numRandom() < 0.5f)
  88. m = -m;
  89. uv[i].step = m;
  90. // printf("%g %g\n",uv[i].relative_amplitude,period);
  91. }
  92. float max_speed = powf(2.0f, unison_bandwidth_cents / 1200.0f);
  93. unison_amplitude_samples = 0.125f * (max_speed - 1.0f)
  94. * samplerate_f / base_freq;
  95. //If functions exceed this limit, they should have requested a bigguer delay
  96. //and thus are buggy
  97. if(unison_amplitude_samples >= max_delay - 1) {
  98. warnx("BUG: Unison amplitude samples too big");
  99. warnx("Unision max_delay should be larger");
  100. unison_amplitude_samples = max_delay - 2;
  101. }
  102. updateUnisonData();
  103. }
  104. void Unison::process(int bufsize, float *inbuf, float *outbuf)
  105. {
  106. if(!uv)
  107. return;
  108. if(!outbuf)
  109. outbuf = inbuf;
  110. float volume = 1.0f / sqrtf(unison_size);
  111. float xpos_step = 1.0f / (float) update_period_samples;
  112. float xpos = (float) update_period_sample_k * xpos_step;
  113. for(int i = 0; i < bufsize; ++i) {
  114. if(update_period_sample_k++ >= update_period_samples) {
  115. updateUnisonData();
  116. update_period_sample_k = 0;
  117. xpos = 0.0f;
  118. }
  119. xpos += xpos_step;
  120. float in = inbuf[i], out = 0.0f;
  121. float sign = 1.0f;
  122. for(int k = 0; k < unison_size; ++k) {
  123. float vpos = uv[k].realpos1 * (1.0f - xpos) + uv[k].realpos2 * xpos; //optimize
  124. float pos = (float)(delay_k + max_delay) - vpos - 1.0f;
  125. int posi;
  126. F2I(pos, posi); //optimize!
  127. int posi_next = posi + 1;
  128. if(posi >= max_delay)
  129. posi -= max_delay;
  130. if(posi_next >= max_delay)
  131. posi_next -= max_delay;
  132. float posf = pos - floorf(pos);
  133. out += ((1.0f - posf) * delay_buffer[posi] + posf
  134. * delay_buffer[posi_next]) * sign;
  135. sign = -sign;
  136. }
  137. outbuf[i] = out * volume;
  138. // printf("%d %g\n",i,outbuf[i]);
  139. delay_buffer[delay_k] = in;
  140. delay_k = (++delay_k < max_delay) ? delay_k : 0;
  141. }
  142. }
  143. void Unison::updateUnisonData()
  144. {
  145. if(!uv)
  146. return;
  147. for(int k = 0; k < unison_size; ++k) {
  148. float pos = uv[k].position;
  149. float step = uv[k].step;
  150. pos += step;
  151. if(pos <= -1.0f) {
  152. pos = -1.0f;
  153. step = -step;
  154. }
  155. else
  156. if(pos >= 1.0f) {
  157. pos = 1.0f;
  158. step = -step;
  159. }
  160. float vibratto_val = (pos - 0.333333333f * pos * pos * pos) * 1.5f; //make the vibratto lfo smoother
  161. //Relative amplitude is utilized, so the delay may be larger than the
  162. //whole buffer, if the buffer is too small, this indicates a buggy call
  163. //to Unison()
  164. float newval = 1.0f + 0.5f
  165. * (vibratto_val + 1.0f) * unison_amplitude_samples
  166. * uv[k].relative_amplitude;
  167. if(first_time)
  168. uv[k].realpos1 = uv[k].realpos2 = newval;
  169. else {
  170. uv[k].realpos1 = uv[k].realpos2;
  171. uv[k].realpos2 = newval;
  172. }
  173. uv[k].position = pos;
  174. uv[k].step = step;
  175. }
  176. first_time = false;
  177. }
  178. }