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.

283 lines
9.0KB

  1. //***********************************************************************************************
  2. //
  3. //TremoloStereoFx module for VCV Rack by Alfredo Santamaria - AS - https://github.com/AScustomWorks/AS
  4. //
  5. //LFO code adapted from the Fundamentals plugins by Andrew Belt http://www.vcvrack.com
  6. //***********************************************************************************************
  7. #include "AS.hpp"
  8. #include "dsp/digital.hpp"
  9. //LFO CODE *****************************
  10. struct LowFrequencyoscillator {
  11. float phase = 0.0f;
  12. float pw = 0.5f;
  13. float freq = 1.0f;
  14. bool offset = false;
  15. bool invert = false;
  16. SchmittTrigger resetTrigger;
  17. LowFrequencyoscillator() {
  18. }
  19. void setPitch(float pitch) {
  20. pitch = fminf(pitch, 8.0f);
  21. freq = powf(2.0f, pitch);
  22. }
  23. void setPulseWidth(float pw_) {
  24. const float pwMin = 0.01f;
  25. pw = clamp(pw_, pwMin, 1.0f - pwMin);
  26. }
  27. void setReset(float reset) {
  28. if (resetTrigger.process(reset)) {
  29. phase = 0.0f;
  30. }
  31. }
  32. void step(float dt) {
  33. float deltaPhase = fminf(freq * dt, 0.5f);
  34. phase += deltaPhase;
  35. if (phase >= 1.0f)
  36. phase -= 1.0f;
  37. }
  38. float sin() {
  39. if (offset)
  40. return 1.0f - cosf(2*M_PI * phase) * (invert ? -1.0f : 1.0f);
  41. else
  42. return sinf(2.0f*M_PI * phase) * (invert ? -1.0f : 1.0f);
  43. }
  44. float tri(float x) {
  45. return 4.0f * fabsf(x - roundf(x));
  46. }
  47. float tri() {
  48. if (offset)
  49. return tri(invert ? phase - 0.5 : phase);
  50. else
  51. return -1.0f + tri(invert ? phase - 0.25f : phase - 0.75f);
  52. }
  53. float light() {
  54. return sinf(2.0f*M_PI * phase);
  55. }
  56. };
  57. //LFO CODE *****************************
  58. struct TremoloStereoFx : Module{
  59. enum ParamIds {
  60. WAVE_PARAM,
  61. FREQ_PARAM,
  62. BLEND_PARAM,
  63. INVERT_PARAM,
  64. BYPASS_SWITCH,
  65. NUM_PARAMS
  66. };
  67. enum InputIds {
  68. SIGNAL_INPUT_L,
  69. SIGNAL_INPUT_R,
  70. WAVE_CV_INPUT,
  71. FREQ_CV_INPUT,
  72. BLEND_CV_INPUT,
  73. BYPASS_CV_INPUT,
  74. RESET_CV_INPUT,
  75. NUM_INPUTS
  76. };
  77. enum OutputIds {
  78. SIGNAL_OUTPUT_L,
  79. SIGNAL_OUTPUT_R,
  80. NUM_OUTPUTS
  81. };
  82. enum LightIds {
  83. WAVE_LIGHT,
  84. PHASE_POS_LIGHT,
  85. PHASE_NEG_LIGHT,
  86. BLEND_LIGHT,
  87. BYPASS_LED,
  88. NUM_LIGHTS
  89. };
  90. LowFrequencyoscillator oscillatorL, oscillatorR;
  91. SchmittTrigger bypass_button_trig;
  92. SchmittTrigger bypass_cv_trig;
  93. bool fx_bypass = false;
  94. float fade_in_fx = 0.0f;
  95. float fade_in_dry = 0.0f;
  96. float fade_out_fx = 1.0f;
  97. float fade_out_dry = 1.0f;
  98. const float fade_speed = 0.001f;
  99. TremoloStereoFx() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  100. void step() override;
  101. json_t *toJson()override {
  102. json_t *rootJm = json_object();
  103. json_t *statesJ = json_array();
  104. json_t *bypassJ = json_boolean(fx_bypass);
  105. json_array_append_new(statesJ, bypassJ);
  106. json_object_set_new(rootJm, "as_FxBypass", statesJ);
  107. return rootJm;
  108. }
  109. void fromJson(json_t *rootJm)override {
  110. json_t *statesJ = json_object_get(rootJm, "as_FxBypass");
  111. json_t *bypassJ = json_array_get(statesJ, 0);
  112. fx_bypass = !!json_boolean_value(bypassJ);
  113. }
  114. void resetFades(){
  115. fade_in_fx = 0.0f;
  116. fade_in_dry = 0.0f;
  117. fade_out_fx = 1.0f;
  118. fade_out_dry = 1.0f;
  119. }
  120. float input_signal_L = 0.0f;
  121. float output_signal_L = 0.0f;
  122. float input_signal_R = 0.0f;
  123. float output_signal_R = 0.0f;
  124. float tremolo_signal_L = 0.0f;
  125. float tremolo_signal_R = 0.0f;
  126. float blend_control = 0.0f;
  127. float lfo_modulation_L = 0.0f;
  128. float lfo_modulation_R = 0.0f;
  129. };
  130. void TremoloStereoFx::step() {
  131. if (bypass_button_trig.process(params[BYPASS_SWITCH].value) || bypass_cv_trig.process(inputs[BYPASS_CV_INPUT].value) ){
  132. fx_bypass = !fx_bypass;
  133. resetFades();
  134. }
  135. lights[BYPASS_LED].value = fx_bypass ? 1.0f : 0.0f;
  136. input_signal_L = clamp(inputs[SIGNAL_INPUT_L].value,-10.0f,10.0f);
  137. if(!inputs[SIGNAL_INPUT_R].active){
  138. input_signal_R = input_signal_L;
  139. }else{
  140. input_signal_R = clamp(inputs[SIGNAL_INPUT_R].value,-10.0f,10.0f);
  141. }
  142. float lfo_pitch = clamp(params[FREQ_PARAM].value + inputs[FREQ_CV_INPUT].value, 0.0f, 3.5f);
  143. //LFO L
  144. oscillatorL.setPitch( lfo_pitch );
  145. oscillatorL.offset = (0.0f);
  146. oscillatorL.invert = (params[INVERT_PARAM].value <= 0.0f);
  147. oscillatorL.setPulseWidth(0.5f);
  148. oscillatorL.step(1.0f / engineGetSampleRate());
  149. oscillatorL.setReset(inputs[RESET_CV_INPUT].value);
  150. //LFO R
  151. oscillatorR.setPitch( lfo_pitch );
  152. oscillatorR.offset = (0.0f);
  153. oscillatorR.invert = false;
  154. oscillatorR.setPulseWidth(0.5f);
  155. oscillatorR.step(1.0f / engineGetSampleRate());
  156. oscillatorR.setReset(inputs[RESET_CV_INPUT].value);
  157. float wave = clamp( params[WAVE_PARAM].value + inputs[WAVE_CV_INPUT].value, 0.0f, 1.0f );
  158. float interp_L = crossfade(oscillatorL.sin(), oscillatorL.tri(), wave);
  159. float interp_R = crossfade(oscillatorR.sin(), oscillatorR.tri(), wave);
  160. lfo_modulation_L = 5.0f * interp_L;
  161. lfo_modulation_R = 5.0f * interp_R;
  162. tremolo_signal_L = input_signal_L * clamp(lfo_modulation_L/10.0f, 0.0f, 1.0f);
  163. tremolo_signal_R = input_signal_R * clamp(lfo_modulation_R/10.0f, 0.0f, 1.0f);
  164. blend_control = clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f);
  165. output_signal_L = crossfade(input_signal_L,tremolo_signal_L,blend_control);
  166. output_signal_R = crossfade(input_signal_R,tremolo_signal_R,blend_control);
  167. //check bypass switch status
  168. if (fx_bypass){
  169. fade_in_dry += fade_speed;
  170. if ( fade_in_dry > 1.0f ) {
  171. fade_in_dry = 1.0f;
  172. }
  173. fade_out_fx -= fade_speed;
  174. if ( fade_out_fx < 0.0f ) {
  175. fade_out_fx = 0.0f;
  176. }
  177. outputs[SIGNAL_OUTPUT_L].value = ( input_signal_L * fade_in_dry ) + ( output_signal_L * fade_out_fx );
  178. outputs[SIGNAL_OUTPUT_R].value = ( input_signal_R * fade_in_dry ) + ( output_signal_R * fade_out_fx );
  179. }else{
  180. fade_in_fx += fade_speed;
  181. if ( fade_in_fx > 1.0f ) {
  182. fade_in_fx = 1.0f;
  183. }
  184. fade_out_dry -= fade_speed;
  185. if ( fade_out_dry < 0.0f ) {
  186. fade_out_dry = 0.0f;
  187. }
  188. outputs[SIGNAL_OUTPUT_L].value = ( input_signal_L * fade_out_dry ) + ( output_signal_L * fade_in_fx );
  189. outputs[SIGNAL_OUTPUT_R].value = ( input_signal_R * fade_out_dry ) + ( output_signal_R * fade_in_fx );
  190. }
  191. lights[PHASE_POS_LIGHT].setBrightnessSmooth(fmaxf(0.0f, oscillatorL.light()));
  192. lights[PHASE_NEG_LIGHT].setBrightnessSmooth(fmaxf(0.0f, -oscillatorL.light()));
  193. lights[BLEND_LIGHT].value = clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f);
  194. }
  195. struct TremoloStereoFxWidget : ModuleWidget
  196. {
  197. TremoloStereoFxWidget(TremoloStereoFx *module);
  198. };
  199. TremoloStereoFxWidget::TremoloStereoFxWidget(TremoloStereoFx *module) : ModuleWidget(module) {
  200. setPanel(SVG::load(assetPlugin(plugin, "res/TremoloStereo.svg")));
  201. //SCREWS
  202. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, 0)));
  203. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  204. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  205. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  206. //phase switch
  207. addParam(ParamWidget::create<as_CKSS>(Vec(13, 100), module, TremoloStereoFx::INVERT_PARAM, 0.0f, 1.0f, 1.0f));
  208. //KNOBS
  209. addParam(ParamWidget::create<as_FxKnobWhite>(Vec(43, 60), module, TremoloStereoFx::WAVE_PARAM, 0.0f, 1.0f, 0.5f));
  210. addParam(ParamWidget::create<as_FxKnobWhite>(Vec(43, 125), module, TremoloStereoFx::FREQ_PARAM, 0.0f, 3.5f, 1.75f));
  211. addParam(ParamWidget::create<as_FxKnobWhite>(Vec(43, 190), module, TremoloStereoFx::BLEND_PARAM, 0.0f, 1.0f, 0.5f));
  212. //LIGHTS
  213. addChild(ModuleLightWidget::create<SmallLight<YellowRedLight>>(Vec(39, 122), module, TremoloStereoFx::PHASE_POS_LIGHT));
  214. addChild(ModuleLightWidget::create<SmallLight<YellowLight>>(Vec(39, 187), module, TremoloStereoFx::BLEND_LIGHT));
  215. //CV INPUTS
  216. addInput(Port::create<as_PJ301MPort>(Vec(10, 67), Port::INPUT, module, TremoloStereoFx::WAVE_CV_INPUT));
  217. addInput(Port::create<as_PJ301MPort>(Vec(10, 132), Port::INPUT, module, TremoloStereoFx::FREQ_CV_INPUT));
  218. addInput(Port::create<as_PJ301MPort>(Vec(10, 197), Port::INPUT, module, TremoloStereoFx::BLEND_CV_INPUT));
  219. //INPUTS
  220. addInput(Port::create<as_PJ301MPort>(Vec(15, 300), Port::INPUT, module, TremoloStereoFx::SIGNAL_INPUT_L));
  221. addInput(Port::create<as_PJ301MPort>(Vec(15, 330), Port::INPUT, module, TremoloStereoFx::SIGNAL_INPUT_R));
  222. //OUTPUTS
  223. addOutput(Port::create<as_PJ301MPort>(Vec(50, 300), Port::OUTPUT, module, TremoloStereoFx::SIGNAL_OUTPUT_L));
  224. addOutput(Port::create<as_PJ301MPort>(Vec(50, 330), Port::OUTPUT, module, TremoloStereoFx::SIGNAL_OUTPUT_R));
  225. //RESET CV
  226. addInput(Port::create<as_PJ301MPort>(Vec(6, 259), Port::INPUT, module, TremoloStereoFx::RESET_CV_INPUT));
  227. //BYPASS CV INPUT
  228. addInput(Port::create<as_PJ301MPort>(Vec(33.5, 259), Port::INPUT, module, TremoloStereoFx::BYPASS_CV_INPUT));
  229. //BYPASS SWITCH
  230. addParam(ParamWidget::create<LEDBezel>(Vec(61, 260), module, TremoloStereoFx::BYPASS_SWITCH , 0.0f, 1.0f, 0.0f));
  231. addChild(ModuleLightWidget::create<LedLight<RedLight>>(Vec(63.2, 262.2), module, TremoloStereoFx::BYPASS_LED));
  232. }
  233. RACK_PLUGIN_MODEL_INIT(AS, TremoloStereoFx) {
  234. Model *modelTremoloStereoFx = Model::create<TremoloStereoFx, TremoloStereoFxWidget>("AS", "TremoloStereoFx", "Tremolo Stereo FX", EFFECT_TAG);
  235. return modelTremoloStereoFx;
  236. }