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.

478 lines
22KB

  1. #include "FrozenWasteland.hpp"
  2. #include "dsp/samplerate.hpp"
  3. #include "dsp/digital.hpp"
  4. #include "dsp-delay/ringbuffer.hpp"
  5. #include <iostream>
  6. #define HISTORY_SIZE (1<<22)
  7. #define NUM_TAPS 64
  8. #define CHANNELS 2
  9. #define DIVISIONS 21
  10. #define NUM_PATTERNS 16
  11. #define NUM_FEEDBACK_TYPES 4
  12. namespace rack_plugin_FrozenWasteland {
  13. struct HairPick : Module {
  14. typedef float T;
  15. enum ParamIds {
  16. CLOCK_DIV_PARAM,
  17. SIZE_PARAM,
  18. PATTERN_TYPE_PARAM,
  19. NUMBER_TAPS_PARAM,
  20. EDGE_LEVEL_PARAM,
  21. TENT_LEVEL_PARAM,
  22. TENT_TAP_PARAM,
  23. FEEDBACK_TYPE_PARAM,
  24. FEEDBACK_AMOUNT_PARAM,
  25. NUM_PARAMS
  26. };
  27. enum InputIds {
  28. CLOCK_INPUT,
  29. CLOCK_DIVISION_CV_INPUT,
  30. VOLT_OCTAVE_INPUT,
  31. SIZE_CV_INPUT,
  32. PATTERN_TYPE_CV_INPUT,
  33. NUMBER_TAPS_CV_INPUT,
  34. EDGE_LEVEL_CV_INPUT,
  35. TENT_LEVEL_CV_INPUT,
  36. TENT_TAP_CV_INPUT,
  37. FEEDBACK_TYPE_CV_INPUT,
  38. FEEDBACK_CV_INPUT,
  39. IN_L_INPUT,
  40. IN_R_INPUT,
  41. NUM_INPUTS
  42. };
  43. enum OutputIds {
  44. OUT_L_OUTPUT,
  45. OUT_R_OUTPUT,
  46. DELAY_LENGTH_OUTPUT,
  47. NUM_OUTPUTS
  48. };
  49. enum LightIds {
  50. NUM_LIGHTS
  51. };
  52. enum FeedbackTypes {
  53. FEEDBACK_GUITAR,
  54. FEEDBACK_SITAR,
  55. FEEDBACK_CLARINET,
  56. FEEDBACK_RAW,
  57. };
  58. const char* combPatternNames[NUM_PATTERNS] = {"Uniform","Flat Middle","Early Comb","Fibonacci","Flat Comb","Late Comb","Rev. Fibonacci","Ess Comb","Rand Uniform","Rand Middle","Rand Early","Rand Fibonacci","Rand Flat","Rand Late","Rand Rev Fib","Rand Ess"};
  59. const float combPatterns[NUM_PATTERNS][NUM_TAPS] = {
  60. {1.0f,2.0f,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0}, // Uniform
  61. {1.0f,2.0f,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,24.5,25.0,25.5,26.0,26.5,27.0,27.5,28.0,28.5,29.0,29.5,30.0,30.5,31.5,32.0,32.5,33.0,33.5,34.0,34.5,35.0,35.5,36.0,36.5,37.0,37.5,38.0,38.5,39.0,39.5,40.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0}, // Flat Middle
  62. {0.25f,0.5f,0.75f,1.0f,1.25f,1.5f,1.75f,2.0f,2.25f,2.5f,2.75f,3.0f,3.25f,3.5f,3.75f,4.0f,4.5f,5.0f,5.5f,6.0f,6.5f,7.0f,7.5f,8.0f,8.5f,9.0f,9.5f,10.0f,10.5f,11.0f,11.5f,12.0f,13.0f,14.0f,15.0f,16.0f,17.0f,18.0f,19.0f,20.0f,21.0f,22.0f,23.0f,24.0f,25.0f,26.0f,27.0f,28.0f,30.0f,32.0f,34.0f,36.0f,38.0f,40.0f,42.0f,44.0f,46.0f,48.0f,50.0f,52.0f,55.0f,58.0f,61.0f,64.0f}, // Early Comb
  63. {0.031f,0.037f,0.043f,0.05f,0.056f,0.062f,0.068f,0.074f,0.087f,0.099f,0.111f,0.124f,0.142f,0.161f,0.18f,0.198f,0.229f,0.26f,0.291f,0.322f,0.372f,0.421f,0.471f,0.52f,0.601f,0.681f,0.762f,0.842f,0.972f,1.102f,1.232f,1.362f,1.573f,1.783f,1.994f,2.204f,2.545f,2.885f,3.226f,3.567f,4.118f,4.669f,5.22f,5.771f,6.663f,7.554f,8.446f,9.337f,10.78f,12.223f,13.666f,15.108f,17.443f,19.777f,22.111f,24.446f,28.223f,32.0f,35.777f,39.554f,45.666f,51.777f,57.889f,64.0f}, // Fibonacci
  64. {2.0f,4.0f,6.0,8.0,10.0,12.0,14.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,24.5,25.0,25.5,26.0,26.5,27.0,27.5,28.0,28.5,29.0,29.5,30.0,30.5,31.0,31.5,32.0,32.5,33.0,33.5,34.0,34.5,35.0,35.5,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,50.0,52.0,54.0,56.0,58.0,60.0,62.0,64.0}, // Flat Comb
  65. {3.0f,6.0f,9.0f,12.0f,14.0f,16.0f,18.0f,20.0f,22.0f,24.0f,26.0f,28.0f,30.0f,32.0f,34.0f,36.0f,37.0f,38.0f,39.0f,40.0f,41.0f,42.0f,43.0f,44.0f,45.0f,46.0f,47.0f,48.0f,49.0f,50.0f,51.0f,52.0f,52.5f,53.0f,53.5f,54.0f,54.5f,55.0f,55.5f,56.0f,56.5f,57.0f,57.5f,58.0f,58.5f,59.0f,59.5f,60.0f,60.25f,60.5f,60.75f,61.0f,61.25f,61.5f,61.75f,62.0f,62.25f,62.5f,62.75f,63.0f,63.25f,63.5f,63.75f,64.0f}, // Late Comb
  66. {0.031f,6.142f,12.254f,18.365f,24.477f,28.254f,32.031f,35.808f,39.585f,41.92f,44.254f,46.588f,48.923f,50.365f,51.808f,53.251f,54.693f,55.585f,56.477f,57.368f,58.26f,58.811f,59.362f,59.913f,60.464f,60.805f,61.146f,61.486f,61.827f,62.037f,62.248f,62.458f,62.669f,62.799f,62.929f,63.059f,63.189f,63.269f,63.35f,63.43f,63.511f,63.56f,63.61f,63.659f,63.709f,63.74f,63.771f,63.802f,63.833f,63.851f,63.87f,63.889f,63.907f,63.92f,63.932f,63.944f,63.957f,63.963f,63.969f,63.975f,63.981f,63.988f,63.994f,64.0f}, // Rev Fibonacci
  67. {0.5f, 1.0f, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 9.0, 10.0, 11.0, 12.0,13.0, 14.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0, 26.0, 30.0, 32.0, 34.0, 36.0, 38.0, 40.0, 42.0, 44.0,46.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 53.5, 54.0, 55.0, 55.5, 67.0, 56.5, 57.0, 57.5, 58.0, 58.5,59.0, 59.5, 60.0, 60.5, 61.0, 61.5, 62.0, 62.5, 63.5,64.0}, // Ess Comb
  68. {1.258f,2.867f,3.545f,4.461f,5.618f,6.916f,7.936f,8.923f,9.572f,10.993f,11.38f,12.026f,13.936f,14.892f,15.736f,16.014f,17.361f,18.787f,19.587f,20.646f,21.276f,22.949f,23.776f,24.212f,25.823f,26.169f,27.944f,28.69f,29.081f,30.472f,31.842f,32.739f,33.38f,34.703f,35.724f,36.334f,37.394f,38.256f,39.527f,40.965f,41.654f,42.668f,43.56f,44.402f,45.806f,46.53f,47.313f,48.504f,49.486f,50.401f,51.495f,52.71f,53.992f,54.407f,55.861f,56.309f,57.091f,58.333f,59.638f,60.449f,61.972f,62.246f,63.842f,64.0f}, // Random Uniform
  69. {1.255f,2.488f,3.252f,4.298f,5.917f,6.708f,7.255f,8.082f,9.828f,10.863f,11.181f,12.218f,13.025f,14.911f,15.891f,16.885f,25.203f,25.61f,25.888f,26.543f,27.024f,27.042f,28.272f,28.45f,29.018f,29.213f,30.248f,30.414f,30.619f,32.049f,32.299f,33.134f,33.184f,33.506f,34.738f,35.368f,35.86f,36.116f,36.729f,37.229f,37.243f,37.79f,38.802f,39.038f,39.413f,40.111f,40.574f,48.727f,49.52f,50.863f,51.137f,52.111f,53.015f,54.411f,55.078f,56.231f,57.537f,58.981f,59.585f,60.899f,61.794f,62.007f,63.964f,64.0f}, // Random Flat Middle
  70. {0.519f,0.907f,1.046f,1.428f,1.513f,1.761f,1.778f,2.46f,2.532f,2.632f,3.049f,3.806f,4.057f,4.229f,4.381f,4.643f,4.812f,6.0f,6.408f,6.961f,7.386f,7.437f,8.008f,8.767f,9.292f,9.534f,9.947f,10.775f,11.216f,11.256f,12.19f,12.835f,13.474f,14.255f,15.6f,16.858f,17.551f,18.903f,19.838f,20.843f,21.639f,22.448f,23.15f,24.456f,25.991f,26.898f,27.615f,28.734f,30.359f,32.205f,34.68f,36.531f,38.956f,40.711f,42.76f,44.832f,46.899f,48.996f,50.255f,52.644f,55.831f,58.236f,61.704f,64.0f}, // Random Early Comb
  71. {0.353f,0.4f,0.408f,0.435f,0.458f,0.516f,0.529f,0.545f,0.557f,0.624f,0.626f,0.631f,0.695f,0.701f,0.705f,0.812f,0.823f,0.843f,0.87f,0.88f,1.028f,1.034f,1.066f,1.115f,1.168f,1.177f,1.32f,1.355f,1.588f,1.629f,1.64f,1.667f,2.343f,2.579f,2.599f,3.182f,3.268f,3.395f,3.697f,4.066f,5.02f,5.276f,5.329f,6.487f,6.749f,7.659f,9.04f,9.645f,10.997f,12.85f,13.996f,15.983f,18.059f,20.243f,22.64f,24.964f,28.876f,32.715f,35.883f,40.513f,46.186f,51.974f,57.948f,64.0f}, // Random Fibonacci
  72. {2.748f,4.499f,6.144f,8.714f,10.868f,12.113f,14.522f,16.561f,17.24f,18.734f,19.323f,20.399f,21.06f,22.009f,23.648f,24.649f,25.162f,25.282f,25.916f,26.128f,26.515f,27.296f,27.521f,28.801f,29.097f,29.135f,29.803f,30.726f,31.407f,31.455f,32.31f,32.72f,33.135f,33.189f,33.761f,34.614f,35.094f,35.548f,36.104f,40.283f,40.338f,41.026f,41.325f,42.119f,42.227f,43.504f,43.758f,44.016f,44.263f,45.679f,45.827f,46.661f,46.991f,47.772f,47.962f,48.453f,50.478f,52.343f,54.117f,56.733f,58.81f,60.796f,62.606f,64.0f}, // Random Flat Comb
  73. {3.295f,6.695f,9.844f,12.738f,14.884f,16.065f,18.612f,20.819f,22.418f,24.964f,26.659f,28.592f,30.977f,32.615f,34.054f,36.412f,37.115f,38.85f,39.301f,40.246f,41.363f,42.518f,43.872f,44.889f,45.561f,46.958f,47.21f,48.644f,49.094f,50.385f,50.443f,51.001f,52.025f,52.201f,53.049f,53.684f,53.732f,54.015f,55.074f,55.077f,55.563f,56.796f,57.155f,57.254f,57.687f,58.124f,59.307f,59.479f,60.035f,60.294f,60.348f,60.517f,60.707f,61.066f,61.553f,61.778f,61.926f,62.073f,62.456f,62.926f,63.105f,63.468f,63.475f,64.0f}, // Random Late Comb
  74. {0.072f,7.105f,12.843f,18.42f,24.766f,28.496f,32.434f,36.489f,40.163f,42.361f,45.122f,46.641f,49.864f,51.319f,52.808f,53.11f,54.105f,55.209f,56.226f,56.628f,57.493f,58.371f,59.075f,59.561f,59.764f,60.242f,61.078f,61.163f,61.183f,61.63f,61.843f,62.011f,62.132f,62.201f,62.386f,62.553f,62.775f,62.797f,62.869f,62.894f,62.933f,63.032f,63.047f,63.17f,63.206f,63.233f,63.249f,63.291f,63.319f,63.425f,63.443f,63.484f,63.581f,63.59f,63.608f,63.686f,63.714f,63.725f,63.737f,63.828f,63.889f,63.903f,63.905f,64.0f}, // Random Rev Fibonacci
  75. {1.227f,1.69f,2.356f,2.925f,3.035f,3.53f,4.379f,4.581f,4.872f,5.614f,6.15f,6.629f,6.876f,7.199f,8.111f,8.855f,9.744f,10.338f,11.417f,12.745f,13.09f,14.512f,15.197f,16.068f,18.112f,20.342f,22.943f,24.92f,26.793f,30.707f,32.794f,34.89f,36.117f,38.542f,40.327f,42.95f,44.243f,46.082f,48.205f,49.592f,50.992f,51.408f,52.914f,53.784f,53.947f,54.299f,55.104f,55.65f,56.713f,57.184f,58.025f,58.193f,59.061f,59.281f,59.715f,60.494f,61.289f,61.693f,61.813f,62.587f,63.459f,63.58f,63.868f,64.0f} //Random Ess Comb
  76. };
  77. const char* feedbackTypeNames[NUM_FEEDBACK_TYPES] = {"Guitar","Sitar","Clarinet","Raw"};
  78. int combPattern = 0;
  79. int feedbackType = 0;
  80. float edgeLevel = 0.0f;
  81. float tentLevel = 1.0f;
  82. int tentTap = 32;
  83. SchmittTrigger clockTrigger;
  84. float divisions[DIVISIONS] = {1/256.0f,1/192.0f,1/128.0f,1/96.0f,1/64.0f,1/48.0f,1/32.0f,1/24.0f,1/16.0f,1/13.0f,1/12.0f,1/11.0f,1/8.0f,1/7.0f,1/6.0f,1/5.0f,1/4.0f,1/3.0f,1/2.0f,1/1.5f,1};
  85. const char* divisionNames[DIVISIONS] = {"/256","/192","/128","/96","/64","/48","/32","/24","/16","/13","/12","/11","/8","/7","/6","/5","/4","/3","/2","/1.5","x 1"};
  86. int division;
  87. float time = 0.0;
  88. float duration = 0;
  89. float baseDelay;
  90. bool secondClockReceived = false;
  91. bool combActive[NUM_TAPS];
  92. float combLevel[NUM_TAPS];
  93. rack_plugin_FrozenWasteland::MultiTapDoubleRingBuffer<float, HISTORY_SIZE,NUM_TAPS> historyBuffer[CHANNELS];
  94. rack_plugin_FrozenWasteland::DoubleRingBuffer<float, 16> outBuffer[NUM_TAPS][CHANNELS];
  95. SampleRateConverter<1> src;
  96. float lastFeedback[CHANNELS] = {0.0f,0.0f};
  97. float lerp(float v0, float v1, float t) {
  98. return (1 - t) * v0 + t * v1;
  99. }
  100. int muteTap(int tapIndex)
  101. {
  102. int tapNumber = 0;
  103. for (int levelIndex = 0; levelIndex < 6; levelIndex += 1)
  104. {
  105. tapNumber += (tapIndex & (1 << levelIndex)) > 0 ? (1 << (5-levelIndex)) : 0;
  106. }
  107. return 64-tapNumber;
  108. }
  109. float envelope(int tapNumber, float edgeLevel, float tentLevel, int tentNumber) {
  110. float t;
  111. if (tapNumber <= tentNumber)
  112. {
  113. t = (float)tapNumber / tentNumber;
  114. return (1 - t) * edgeLevel + t * tentLevel;
  115. }
  116. else
  117. {
  118. t = (float)(tapNumber-tentNumber) / (63-tentNumber);
  119. return (1 - t) * tentLevel+ t * edgeLevel;
  120. }
  121. }
  122. HairPick() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  123. }
  124. const char* tapNames[NUM_TAPS+2] {"1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","ALL","EXT"};
  125. void step() override;
  126. };
  127. void HairPick::step() {
  128. combPattern = (int)clamp(params[PATTERN_TYPE_PARAM].value + (inputs[PATTERN_TYPE_CV_INPUT].value * 1.5f),0.0f,15.0);
  129. feedbackType = (int)clamp(params[FEEDBACK_TYPE_PARAM].value + (inputs[FEEDBACK_TYPE_CV_INPUT].value / 10.0f),0.0f,3.0);
  130. int tapCount = (int)clamp(params[NUMBER_TAPS_PARAM].value + (inputs[NUMBER_TAPS_CV_INPUT].value * 6.4f),1.0f,64.0);
  131. edgeLevel = clamp(params[EDGE_LEVEL_PARAM].value + (inputs[EDGE_LEVEL_CV_INPUT].value / 10.0f),0.0f,1.0);
  132. tentLevel = clamp(params[TENT_LEVEL_PARAM].value + (inputs[TENT_LEVEL_CV_INPUT].value / 10.0f),0.0f,1.0);
  133. tentTap = (int)clamp(params[TENT_TAP_PARAM].value + (inputs[TENT_TAP_CV_INPUT].value * 6.3f),1.0f,63.0f);
  134. //Initialize muting - set all active first
  135. for(int tapNumber = 0;tapNumber<NUM_TAPS;tapNumber++) {
  136. combActive[tapNumber] = true;
  137. }
  138. //Turn off as needed
  139. for(int tapIndex = NUM_TAPS-1;tapIndex >= tapCount;tapIndex--) {
  140. int tapNumber = muteTap(tapIndex);
  141. combActive[tapNumber] = false;
  142. }
  143. float divisionf = params[CLOCK_DIV_PARAM].value;
  144. if(inputs[CLOCK_DIVISION_CV_INPUT].active) {
  145. divisionf +=(inputs[CLOCK_DIVISION_CV_INPUT].value * (DIVISIONS / 10.0));
  146. }
  147. divisionf = clamp(divisionf,0.0f,20.0f);
  148. division = (DIVISIONS-1) - int(divisionf); //TODO: Reverse Division Order
  149. time += 1.0 / engineGetSampleRate();
  150. if(inputs[CLOCK_INPUT].active) {
  151. if(clockTrigger.process(inputs[CLOCK_INPUT].value)) {
  152. if(secondClockReceived) {
  153. duration = time;
  154. }
  155. time = 0;
  156. //secondClockReceived = true;
  157. secondClockReceived = !secondClockReceived;
  158. }
  159. //lights[CLOCK_LIGHT].value = time > (duration/2.0);
  160. }
  161. if(inputs[CLOCK_INPUT].active) {
  162. baseDelay = duration / divisions[division];
  163. } else {
  164. baseDelay = clamp(params[SIZE_PARAM].value, 0.001f, 10.0f);
  165. }
  166. float pitchShift = powf(2.0f,inputs[VOLT_OCTAVE_INPUT].value);
  167. baseDelay = baseDelay / pitchShift;
  168. outputs[DELAY_LENGTH_OUTPUT].value = baseDelay;
  169. for(int channel = 0;channel < CHANNELS;channel++) {
  170. // Get input to delay block
  171. float in = 0.0f;
  172. if(channel == 0) {
  173. in = inputs[IN_L_INPUT].value;
  174. } else {
  175. in = inputs[IN_R_INPUT].active ? inputs[IN_R_INPUT].value : inputs[IN_L_INPUT].value;
  176. }
  177. float feedbackAmount = clamp(params[FEEDBACK_AMOUNT_PARAM].value + (inputs[FEEDBACK_CV_INPUT].value / 10.0f), 0.0f, 1.0f);
  178. float feedbackInput = lastFeedback[channel];
  179. float dry = in + feedbackInput * feedbackAmount;
  180. // Push dry sample into history buffer
  181. if (!historyBuffer[channel].full(NUM_TAPS-1)) {
  182. historyBuffer[channel].push(dry);
  183. }
  184. float delayNonlinearity = 1.0f;
  185. float percentChange = 10.0f;
  186. //Apply non-linearity
  187. if(feedbackType == FEEDBACK_SITAR) {
  188. if(in > 0) {
  189. delayNonlinearity = 1 + (in/(10.0f * 100.0f)*percentChange); //Test sitar will change length by 1%
  190. }
  191. }
  192. float wet = 0.0f; // This is the mix of delays and input that is outputed
  193. float feedbackValue = 0.0f; // This is the output of a tap that gets sent back to input
  194. for(int tap = 0; tap < NUM_TAPS;tap++) {
  195. int inFrames = min(historyBuffer[channel].size(tap), 16);
  196. // Compute delay time in seconds
  197. float delay = baseDelay * delayNonlinearity * combPatterns[combPattern][tap] / NUM_TAPS;
  198. //float delayMod = 0.0f;
  199. // Number of delay samples
  200. float index = delay * engineGetSampleRate();
  201. // How many samples do we need consume to catch up?
  202. float consume = index - historyBuffer[channel].size(tap);
  203. if (outBuffer[tap][channel].empty()) {
  204. double ratio = 1.0;
  205. if (consume <= -16)
  206. ratio = 0.5;
  207. else if (consume >= 16)
  208. ratio = 2.0;
  209. float inSR = engineGetSampleRate();
  210. float outSR = ratio * inSR;
  211. int outFrames = outBuffer[tap][channel].capacity();
  212. src.setRates(inSR, outSR);
  213. src.process((const Frame<1>*)historyBuffer[channel].startData(tap), &inFrames, (Frame<1>*)outBuffer[tap][channel].endData(), &outFrames);
  214. outBuffer[tap][channel].endIncr(outFrames);
  215. historyBuffer[channel].startIncr(tap, inFrames);
  216. }
  217. float wetTap = 0.0f;
  218. if (!outBuffer[tap][channel].empty()) {
  219. wetTap = outBuffer[tap][channel].shift();
  220. if(tap == NUM_TAPS-1) {
  221. feedbackValue = wetTap;
  222. }
  223. if(!combActive[tap]) {
  224. wetTap = 0.0f;
  225. } else {
  226. wetTap = wetTap * envelope(tap,edgeLevel,tentLevel,tentTap);
  227. }
  228. }
  229. wet += wetTap;
  230. }
  231. wet = wet / ((float)tapCount) * sqrt((float)tapCount);
  232. float feedbackWeight = 0.5;
  233. switch(feedbackType) {
  234. case FEEDBACK_GUITAR :
  235. feedbackValue = (feedbackWeight * feedbackValue) + ((1-feedbackWeight) * lastFeedback[channel]);
  236. break;
  237. case FEEDBACK_SITAR :
  238. feedbackValue = (feedbackWeight * feedbackValue) + ((1-feedbackWeight) * lastFeedback[channel]);
  239. break;
  240. case FEEDBACK_CLARINET :
  241. feedbackValue = (feedbackWeight * feedbackValue) + ((1-feedbackWeight) * lastFeedback[channel]);
  242. break;
  243. case FEEDBACK_RAW :
  244. //feedbackValue = wet;
  245. break;
  246. }
  247. //feedbackValue = clamp(feedbackValue,-10.0f,10.0f);
  248. lastFeedback[channel] = feedbackValue;
  249. float out = wet;
  250. outputs[OUT_L_OUTPUT + channel].value = out;
  251. }
  252. }
  253. struct HPStatusDisplay : TransparentWidget {
  254. HairPick *module;
  255. int frame = 0;
  256. std::shared_ptr<Font> fontNumbers,fontText;
  257. HPStatusDisplay() {
  258. fontNumbers = Font::load(assetPlugin(plugin, "res/fonts/01 Digit.ttf"));
  259. fontText = Font::load(assetPlugin(plugin, "res/fonts/DejaVuSansMono.ttf"));
  260. }
  261. void drawDivision(NVGcontext *vg, Vec pos, int division) {
  262. nvgFontSize(vg, 20);
  263. nvgFontFaceId(vg, fontNumbers->handle);
  264. nvgTextLetterSpacing(vg, -2);
  265. nvgFillColor(vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
  266. char text[128];
  267. snprintf(text, sizeof(text), "%s", module->divisionNames[division]);
  268. nvgText(vg, pos.x, pos.y, text, NULL);
  269. }
  270. void drawDelayTime(NVGcontext *vg, Vec pos, float delayTime) {
  271. nvgFontSize(vg, 28);
  272. nvgFontFaceId(vg, fontNumbers->handle);
  273. nvgTextLetterSpacing(vg, -2);
  274. nvgFillColor(vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
  275. char text[128];
  276. snprintf(text, sizeof(text), "%6.0f", delayTime*1000);
  277. nvgText(vg, pos.x, pos.y, text, NULL);
  278. }
  279. void drawPatternType(NVGcontext *vg, Vec pos, int patternType) {
  280. nvgFontSize(vg, 14);
  281. nvgFontFaceId(vg, fontText->handle);
  282. nvgTextLetterSpacing(vg, -2);
  283. nvgFillColor(vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
  284. char text[128];
  285. snprintf(text, sizeof(text), "%s", module->combPatternNames[patternType]);
  286. nvgText(vg, pos.x, pos.y, text, NULL);
  287. }
  288. void drawEnvelope(NVGcontext *vg, Vec pos,float edgeLevel, float tentLevel, int tentTap) {
  289. nvgStrokeWidth(vg, 1);
  290. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0x20, 0xff));
  291. nvgBeginPath(vg);
  292. float xOffset = (float)(tentTap-1)/64*100;
  293. nvgMoveTo(vg, pos.x+0, pos.y+20*(1-edgeLevel));
  294. nvgLineTo(vg, pos.x+xOffset, pos.y+20*(1-tentLevel));
  295. nvgLineTo(vg, pos.x+99, pos.y+20*(1-edgeLevel));
  296. nvgStroke(vg);
  297. }
  298. void drawFeedbackType(NVGcontext *vg, Vec pos, int feedbackType) {
  299. nvgFontSize(vg, 14);
  300. nvgFontFaceId(vg, fontText->handle);
  301. nvgTextLetterSpacing(vg, -2);
  302. nvgFillColor(vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
  303. char text[128];
  304. snprintf(text, sizeof(text), "%s", module->feedbackTypeNames[feedbackType]);
  305. nvgText(vg, pos.x, pos.y, text, NULL);
  306. }
  307. void draw(NVGcontext *vg) override {
  308. //drawProgress(vg,module->oscillator.progress());
  309. drawDivision(vg, Vec(91,60), module->division);
  310. drawDelayTime(vg, Vec(350,65), module->baseDelay);
  311. drawPatternType(vg, Vec(64,135), module->combPattern);
  312. drawEnvelope(vg,Vec(55,166),module->edgeLevel,module->tentLevel,module->tentTap);
  313. drawFeedbackType(vg, Vec(60,303), module->feedbackType);
  314. }
  315. };
  316. struct HairPickWidget : ModuleWidget {
  317. HairPickWidget(HairPick *module);
  318. };
  319. HairPickWidget::HairPickWidget(HairPick *module) : ModuleWidget(module) {
  320. box.size = Vec(RACK_GRID_WIDTH*14, RACK_GRID_HEIGHT);
  321. {
  322. SVGPanel *panel = new SVGPanel();
  323. panel->box.size = box.size;
  324. panel->setBackground(SVG::load(assetPlugin(plugin, "res/HairPick.svg")));
  325. addChild(panel);
  326. }
  327. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  328. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  329. addChild(Widget::create<ScrewSilver>(Vec(15, 745)));
  330. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 745)));
  331. {
  332. HPStatusDisplay *display = new HPStatusDisplay();
  333. display->module = module;
  334. display->box.pos = Vec(0, 0);
  335. display->box.size = Vec(box.size.x, 200);
  336. addChild(display);
  337. }
  338. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(45, 33), module, HairPick::CLOCK_DIV_PARAM, 0, DIVISIONS-1, 0));
  339. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(157, 33), module, HairPick::SIZE_PARAM, 0.001f, 10.0f, 0.350f));
  340. //addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(257, 40), module, HairPick::GRID_PARAM, 0.001f, 10.0f, 0.350f));
  341. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(17, 115), module, HairPick::PATTERN_TYPE_PARAM, 0.0f, 15.0f, 0.0f));
  342. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(157, 115), module, HairPick::NUMBER_TAPS_PARAM, 1.0f, 64.0f, 64.0f));
  343. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(17, 200), module, HairPick::EDGE_LEVEL_PARAM, 0.0f, 1.0f, 0.5f));
  344. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(87, 200), module, HairPick::TENT_LEVEL_PARAM, 0.0f, 1.0f, 0.5f));
  345. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(157, 200), module, HairPick::TENT_TAP_PARAM, 0.0f, 63.0f, 32.0f));
  346. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(17, 283), module, HairPick::FEEDBACK_TYPE_PARAM, 0.0f, 3, 0.0f));
  347. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(157, 283), module, HairPick::FEEDBACK_AMOUNT_PARAM, 0.0f, 1.0f, 0.0f));
  348. // addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(474, 189), module, HairPick::PING_PONG_LIGHT));
  349. addInput(Port::create<PJ301MPort>(Vec(12, 32), Port::INPUT, module, HairPick::CLOCK_INPUT));
  350. addInput(Port::create<PJ301MPort>(Vec(12, 74), Port::INPUT, module, HairPick::VOLT_OCTAVE_INPUT));
  351. addInput(Port::create<PJ301MPort>(Vec(50, 74), Port::INPUT, module, HairPick::CLOCK_DIVISION_CV_INPUT));
  352. addInput(Port::create<PJ301MPort>(Vec(162, 74), Port::INPUT, module, HairPick::SIZE_CV_INPUT));
  353. //addInput(Port::create<PJ301MPort>(Vec(300, 45), Port::INPUT, module, HairPick::GRID_CV_INPUT));
  354. addInput(Port::create<PJ301MPort>(Vec(22, 156), Port::INPUT, module, HairPick::PATTERN_TYPE_CV_INPUT));
  355. addInput(Port::create<PJ301MPort>(Vec(162, 156), Port::INPUT, module, HairPick::NUMBER_TAPS_CV_INPUT));
  356. addInput(Port::create<PJ301MPort>(Vec(22, 241), Port::INPUT, module, HairPick::EDGE_LEVEL_CV_INPUT));
  357. addInput(Port::create<PJ301MPort>(Vec(92, 241), Port::INPUT, module, HairPick::TENT_LEVEL_CV_INPUT));
  358. addInput(Port::create<PJ301MPort>(Vec(162, 241), Port::INPUT, module, HairPick::TENT_TAP_CV_INPUT));
  359. addInput(Port::create<PJ301MPort>(Vec(22, 324), Port::INPUT, module, HairPick::FEEDBACK_TYPE_CV_INPUT));
  360. addInput(Port::create<PJ301MPort>(Vec(162, 324), Port::INPUT, module, HairPick::FEEDBACK_CV_INPUT));
  361. addInput(Port::create<PJ301MPort>(Vec(50, 336), Port::INPUT, module, HairPick::IN_L_INPUT));
  362. addInput(Port::create<PJ301MPort>(Vec(75, 336), Port::INPUT, module, HairPick::IN_R_INPUT));
  363. addOutput(Port::create<PJ301MPort>(Vec(110, 336), Port::OUTPUT, module, HairPick::OUT_L_OUTPUT));
  364. addOutput(Port::create<PJ301MPort>(Vec(135, 336), Port::OUTPUT, module, HairPick::OUT_R_OUTPUT));
  365. addOutput(Port::create<PJ301MPort>(Vec(130, 74), Port::OUTPUT, module, HairPick::DELAY_LENGTH_OUTPUT));
  366. }
  367. } // namespace rack_plugin_FrozenWasteland
  368. using namespace rack_plugin_FrozenWasteland;
  369. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, HairPick) {
  370. Model *modelHairPick = Model::create<HairPick, HairPickWidget>("Frozen Wasteland", "HairPick", "Hair Pick", FILTER_TAG);
  371. return modelHairPick;
  372. }