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.

247 lines
7.3KB

  1. //***********************************************************************************************
  2. //
  3. //TremoloFx 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 TremoloFx : 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,
  69. WAVE_CV_INPUT,
  70. FREQ_CV_INPUT,
  71. BLEND_CV_INPUT,
  72. BYPASS_CV_INPUT,
  73. NUM_INPUTS
  74. };
  75. enum OutputIds {
  76. SIGNAL_OUTPUT,
  77. NUM_OUTPUTS
  78. };
  79. enum LightIds {
  80. WAVE_LIGHT,
  81. PHASE_POS_LIGHT,
  82. PHASE_NEG_LIGHT,
  83. BLEND_LIGHT,
  84. BYPASS_LED,
  85. NUM_LIGHTS
  86. };
  87. LowFrequencyOscillator oscillator;
  88. SchmittTrigger bypass_button_trig;
  89. SchmittTrigger bypass_cv_trig;
  90. bool fx_bypass = false;
  91. float fade_in_fx = 0.0f;
  92. float fade_in_dry = 0.0f;
  93. float fade_out_fx = 1.0f;
  94. float fade_out_dry = 1.0f;
  95. const float fade_speed = 0.001f;
  96. TremoloFx() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  97. void step() override;
  98. json_t *toJson()override {
  99. json_t *rootJm = json_object();
  100. json_t *statesJ = json_array();
  101. json_t *bypassJ = json_boolean(fx_bypass);
  102. json_array_append_new(statesJ, bypassJ);
  103. json_object_set_new(rootJm, "as_FxBypass", statesJ);
  104. return rootJm;
  105. }
  106. void fromJson(json_t *rootJm)override {
  107. json_t *statesJ = json_object_get(rootJm, "as_FxBypass");
  108. json_t *bypassJ = json_array_get(statesJ, 0);
  109. fx_bypass = !!json_boolean_value(bypassJ);
  110. }
  111. void resetFades(){
  112. fade_in_fx = 0.0f;
  113. fade_in_dry = 0.0f;
  114. fade_out_fx = 1.0f;
  115. fade_out_dry = 1.0f;
  116. }
  117. float input_signal = 0.0f;
  118. float output_signal = 0.0f;
  119. float tremolo_signal = 0.0f;
  120. float blend_control = 0.0f;
  121. float lfo_modulation = 0.0f;
  122. };
  123. void TremoloFx::step() {
  124. if (bypass_button_trig.process(params[BYPASS_SWITCH].value) || bypass_cv_trig.process(inputs[BYPASS_CV_INPUT].value) ){
  125. fx_bypass = !fx_bypass;
  126. resetFades();
  127. }
  128. lights[BYPASS_LED].value = fx_bypass ? 1.0f : 0.0f;
  129. input_signal = clamp(inputs[SIGNAL_INPUT].value,-10.0f,10.0f);
  130. //oscillator.setPitch(params[FREQ_PARAM].value);
  131. oscillator.setPitch( clamp(params[FREQ_PARAM].value + inputs[FREQ_CV_INPUT].value, 0.0f, 3.5f) );
  132. oscillator.offset = (1.0f);
  133. oscillator.invert = (params[INVERT_PARAM].value <= 0.0f);
  134. oscillator.setPulseWidth(0.5f);
  135. oscillator.step(1.0f / engineGetSampleRate());
  136. float wave = clamp( params[WAVE_PARAM].value + inputs[WAVE_CV_INPUT].value, 0.0f, 1.0f );
  137. float interp = crossfade(oscillator.sin(), oscillator.tri(), wave);
  138. lfo_modulation = 5.0f * interp;
  139. tremolo_signal = input_signal * clamp(lfo_modulation/10.0f, 0.0f, 1.0f);
  140. blend_control = clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f);
  141. output_signal = crossfade(input_signal,tremolo_signal,blend_control);
  142. //check bypass switch status
  143. if (fx_bypass){
  144. fade_in_dry += fade_speed;
  145. if ( fade_in_dry > 1.0f ) {
  146. fade_in_dry = 1.0f;
  147. }
  148. fade_out_fx -= fade_speed;
  149. if ( fade_out_fx < 0.0f ) {
  150. fade_out_fx = 0.0f;
  151. }
  152. outputs[SIGNAL_OUTPUT].value = ( input_signal * fade_in_dry ) + ( output_signal * fade_out_fx );
  153. }else{
  154. fade_in_fx += fade_speed;
  155. if ( fade_in_fx > 1.0f ) {
  156. fade_in_fx = 1.0f;
  157. }
  158. fade_out_dry -= fade_speed;
  159. if ( fade_out_dry < 0.0f ) {
  160. fade_out_dry = 0.0f;
  161. }
  162. outputs[SIGNAL_OUTPUT].value = ( input_signal * fade_out_dry ) + ( output_signal * fade_in_fx );
  163. }
  164. lights[PHASE_POS_LIGHT].setBrightnessSmooth(fmaxf(0.0f, oscillator.light()));
  165. lights[PHASE_NEG_LIGHT].setBrightnessSmooth(fmaxf(0.0f, -oscillator.light()));
  166. lights[BLEND_LIGHT].value = clamp(params[BLEND_PARAM].value + inputs[BLEND_CV_INPUT].value / 10.0f, 0.0f, 1.0f);
  167. }
  168. struct TremoloFxWidget : ModuleWidget
  169. {
  170. TremoloFxWidget(TremoloFx *module);
  171. };
  172. TremoloFxWidget::TremoloFxWidget(TremoloFx *module) : ModuleWidget(module) {
  173. setPanel(SVG::load(assetPlugin(plugin, "res/Tremolo.svg")));
  174. //SCREWS
  175. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, 0)));
  176. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  177. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  178. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  179. //phase switch
  180. addParam(ParamWidget::create<as_CKSS>(Vec(13, 100), module, TremoloFx::INVERT_PARAM, 0.0f, 1.0f, 1.0f));
  181. //KNOBS
  182. addParam(ParamWidget::create<as_FxKnobWhite>(Vec(43, 60), module, TremoloFx::WAVE_PARAM, 0.0f, 1.0f, 0.5f));
  183. addParam(ParamWidget::create<as_FxKnobWhite>(Vec(43, 125), module, TremoloFx::FREQ_PARAM, 0.0f, 3.5f, 1.75f));
  184. addParam(ParamWidget::create<as_FxKnobWhite>(Vec(43, 190), module, TremoloFx::BLEND_PARAM, 0.0f, 1.0f, 0.5f));
  185. //LIGHTS
  186. addChild(ModuleLightWidget::create<SmallLight<YellowRedLight>>(Vec(39, 122), module, TremoloFx::PHASE_POS_LIGHT));
  187. addChild(ModuleLightWidget::create<SmallLight<YellowLight>>(Vec(39, 187), module, TremoloFx::BLEND_LIGHT));
  188. //BYPASS SWITCH
  189. addParam(ParamWidget::create<LEDBezel>(Vec(55, 260), module, TremoloFx::BYPASS_SWITCH , 0.0f, 1.0f, 0.0f));
  190. addChild(ModuleLightWidget::create<LedLight<RedLight>>(Vec(57.2, 262), module, TremoloFx::BYPASS_LED));
  191. //INS/OUTS
  192. addInput(Port::create<as_PJ301MPort>(Vec(10, 310), Port::INPUT, module, TremoloFx::SIGNAL_INPUT));
  193. addOutput(Port::create<as_PJ301MPort>(Vec(55, 310), Port::OUTPUT, module, TremoloFx::SIGNAL_OUTPUT));
  194. //CV INPUTS
  195. addInput(Port::create<as_PJ301MPort>(Vec(10, 67), Port::INPUT, module, TremoloFx::WAVE_CV_INPUT));
  196. addInput(Port::create<as_PJ301MPort>(Vec(10, 132), Port::INPUT, module, TremoloFx::FREQ_CV_INPUT));
  197. addInput(Port::create<as_PJ301MPort>(Vec(10, 197), Port::INPUT, module, TremoloFx::BLEND_CV_INPUT));
  198. //BYPASS CV INPUT
  199. addInput(Port::create<as_PJ301MPort>(Vec(10, 259), Port::INPUT, module, TremoloFx::BYPASS_CV_INPUT));
  200. }
  201. RACK_PLUGIN_MODEL_INIT(AS, TremoloFx) {
  202. Model *modelTremoloFx = Model::create<TremoloFx, TremoloFxWidget>("AS", "TremoloFx", "Tremolo FX", EFFECT_TAG);
  203. return modelTremoloFx;
  204. }