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.

310 lines
12KB

  1. #include "plugin.hpp"
  2. #include "stmlib/dsp/hysteresis_quantizer.h"
  3. #include "stmlib/dsp/units.h"
  4. #include "tides2/poly_slope_generator.h"
  5. #include "tides2/ramp_extractor.h"
  6. #include "tides2/io_buffer.h"
  7. static const float kRootScaled[3] = {
  8. 0.125f,
  9. 2.0f,
  10. 130.81f
  11. };
  12. static const tides2::Ratio kRatios[20] = {
  13. { 0.0625f, 16 },
  14. { 0.125f, 8 },
  15. { 0.1666666f, 6 },
  16. { 0.25f, 4 },
  17. { 0.3333333f, 3 },
  18. { 0.5f, 2 },
  19. { 0.6666666f, 3 },
  20. { 0.75f, 4 },
  21. { 0.8f, 5 },
  22. { 1, 1 },
  23. { 1, 1 },
  24. { 1.25f, 4 },
  25. { 1.3333333f, 3 },
  26. { 1.5f, 2 },
  27. { 2.0f, 1 },
  28. { 3.0f, 1 },
  29. { 4.0f, 1 },
  30. { 6.0f, 1 },
  31. { 8.0f, 1 },
  32. { 16.0f, 1 },
  33. };
  34. struct Tides2 : Module {
  35. enum ParamIds {
  36. RANGE_PARAM,
  37. MODE_PARAM,
  38. RAMP_PARAM,
  39. FREQUENCY_PARAM,
  40. SHAPE_PARAM,
  41. SMOOTHNESS_PARAM,
  42. SLOPE_PARAM,
  43. SHIFT_PARAM,
  44. SLOPE_CV_PARAM,
  45. FREQUENCY_CV_PARAM,
  46. SMOOTHNESS_CV_PARAM,
  47. SHAPE_CV_PARAM,
  48. SHIFT_CV_PARAM,
  49. NUM_PARAMS
  50. };
  51. enum InputIds {
  52. SLOPE_INPUT,
  53. FREQUENCY_INPUT,
  54. V_OCT_INPUT,
  55. SMOOTHNESS_INPUT,
  56. SHAPE_INPUT,
  57. SHIFT_INPUT,
  58. TRIG_INPUT,
  59. CLOCK_INPUT,
  60. NUM_INPUTS
  61. };
  62. enum OutputIds {
  63. ENUMS(OUT_OUTPUTS, 4),
  64. NUM_OUTPUTS
  65. };
  66. enum LightIds {
  67. ENUMS(RANGE_LIGHT, 2),
  68. ENUMS(OUTPUT_MODE_LIGHT, 2),
  69. ENUMS(RAMP_MODE_LIGHT, 2),
  70. ENUMS(OUTPUT_LIGHTS, 4),
  71. NUM_LIGHTS
  72. };
  73. tides2::PolySlopeGenerator poly_slope_generator;
  74. tides2::RampExtractor ramp_extractor;
  75. stmlib::HysteresisQuantizer ratio_index_quantizer;
  76. // State
  77. int range;
  78. tides2::OutputMode output_mode;
  79. tides2::RampMode ramp_mode;
  80. dsp::BooleanTrigger rangeTrigger;
  81. dsp::BooleanTrigger modeTrigger;
  82. dsp::BooleanTrigger rampTrigger;
  83. // Buffers
  84. tides2::PolySlopeGenerator::OutputSample out[tides2::kBlockSize] = {};
  85. stmlib::GateFlags trig_flags[tides2::kBlockSize] = {};
  86. stmlib::GateFlags clock_flags[tides2::kBlockSize] = {};
  87. stmlib::GateFlags previous_trig_flag = stmlib::GATE_FLAG_LOW;
  88. stmlib::GateFlags previous_clock_flag = stmlib::GATE_FLAG_LOW;
  89. bool must_reset_ramp_extractor = true;
  90. tides2::OutputMode previous_output_mode = tides2::OUTPUT_MODE_GATES;
  91. uint8_t frame = 0;
  92. Tides2() {
  93. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  94. configParam(RANGE_PARAM, 0.0, 1.0, 0.0, "Frequency range");
  95. configParam(MODE_PARAM, 0.0, 1.0, 0.0, "Output mode");
  96. configParam(FREQUENCY_PARAM, -48, 48, 0.0, "Frequency");
  97. configParam(SHAPE_PARAM, 0.0, 1.0, 0.5, "Shape");
  98. configParam(RAMP_PARAM, 0.0, 1.0, 0.0, "Ramp mode");
  99. configParam(SMOOTHNESS_PARAM, 0.0, 1.0, 0.5, "Waveshape transformation");
  100. configParam(SLOPE_PARAM, 0.0, 1.0, 0.5, "Ascending/descending ratio");
  101. configParam(SHIFT_PARAM, 0.0, 1.0, 0.5, "Output polarization and shifting");
  102. configParam(SLOPE_CV_PARAM, -1.0, 1.0, 0.0, "Slope CV");
  103. configParam(FREQUENCY_CV_PARAM, -1.0, 1.0, 0.0, "Frequency CV");
  104. configParam(SMOOTHNESS_CV_PARAM, -1.0, 1.0, 0.0, "Smoothness CV");
  105. configParam(SHAPE_CV_PARAM, -1.0, 1.0, 0.0, "Shape CV");
  106. configParam(SHIFT_CV_PARAM, -1.0, 1.0, 0.0, "Shift CV");
  107. poly_slope_generator.Init();
  108. ratio_index_quantizer.Init();
  109. onReset();
  110. onSampleRateChange();
  111. }
  112. void onReset() override {
  113. range = 1;
  114. output_mode = tides2::OUTPUT_MODE_GATES;
  115. ramp_mode = tides2::RAMP_MODE_LOOPING;
  116. }
  117. void onRandomize() override {
  118. range = random::u32() % 3;
  119. output_mode = (tides2::OutputMode)(random::u32() % 4);
  120. ramp_mode = (tides2::RampMode)(random::u32() % 3);
  121. }
  122. void onSampleRateChange() override {
  123. ramp_extractor.Init(APP->engine->getSampleRate(), 40.f / APP->engine->getSampleRate());
  124. }
  125. json_t* dataToJson() override {
  126. json_t* rootJ = json_object();
  127. json_object_set_new(rootJ, "range", json_integer(range));
  128. json_object_set_new(rootJ, "output", json_integer(output_mode));
  129. json_object_set_new(rootJ, "ramp", json_integer(ramp_mode));
  130. return rootJ;
  131. }
  132. void dataFromJson(json_t* rootJ) override {
  133. json_t* rangeJ = json_object_get(rootJ, "range");
  134. if (rangeJ)
  135. range = json_integer_value(rangeJ);
  136. json_t* outputJ = json_object_get(rootJ, "output");
  137. if (outputJ)
  138. output_mode = (tides2::OutputMode) json_integer_value(outputJ);
  139. json_t* rampJ = json_object_get(rootJ, "ramp");
  140. if (rampJ)
  141. ramp_mode = (tides2::RampMode) json_integer_value(rampJ);
  142. }
  143. void process(const ProcessArgs& args) override {
  144. // Switches
  145. if (rangeTrigger.process(params[RANGE_PARAM].getValue() > 0.f)) {
  146. range = (range + 1) % 3;
  147. }
  148. if (modeTrigger.process(params[MODE_PARAM].getValue() > 0.f)) {
  149. output_mode = (tides2::OutputMode)((output_mode + 1) % 4);
  150. }
  151. if (rampTrigger.process(params[RAMP_PARAM].getValue() > 0.f)) {
  152. ramp_mode = (tides2::RampMode)((ramp_mode + 1) % 3);
  153. }
  154. // Input gates
  155. trig_flags[frame] = stmlib::ExtractGateFlags(previous_trig_flag, inputs[TRIG_INPUT].getVoltage() >= 1.7f);
  156. previous_trig_flag = trig_flags[frame];
  157. clock_flags[frame] = stmlib::ExtractGateFlags(previous_clock_flag, inputs[CLOCK_INPUT].getVoltage() >= 1.7f);
  158. previous_clock_flag = clock_flags[frame];
  159. // Process block
  160. if (++frame >= tides2::kBlockSize) {
  161. frame = 0;
  162. tides2::Range range_mode = (range < 2) ? tides2::RANGE_CONTROL : tides2::RANGE_AUDIO;
  163. float note = clamp(params[FREQUENCY_PARAM].getValue() + 12.f * inputs[V_OCT_INPUT].getVoltage(), -96.f, 96.f);
  164. float fm = clamp(params[FREQUENCY_CV_PARAM].getValue() * inputs[FREQUENCY_INPUT].getVoltage() * 12.f, -96.f, 96.f);
  165. float transposition = note + fm;
  166. float ramp[tides2::kBlockSize];
  167. float frequency;
  168. if (inputs[CLOCK_INPUT].isConnected()) {
  169. if (must_reset_ramp_extractor) {
  170. ramp_extractor.Reset();
  171. }
  172. tides2::Ratio r = ratio_index_quantizer.Lookup(kRatios, 0.5f + transposition * 0.0105f, 20);
  173. frequency = ramp_extractor.Process(
  174. range_mode == tides2::RANGE_AUDIO,
  175. range_mode == tides2::RANGE_AUDIO && ramp_mode == tides2::RAMP_MODE_AR,
  176. r,
  177. clock_flags,
  178. ramp,
  179. tides2::kBlockSize);
  180. must_reset_ramp_extractor = false;
  181. }
  182. else {
  183. frequency = kRootScaled[range] / args.sampleRate * stmlib::SemitonesToRatio(transposition);
  184. must_reset_ramp_extractor = true;
  185. }
  186. // Get parameters
  187. float slope = clamp(params[SLOPE_PARAM].getValue() + dsp::cubic(params[SLOPE_CV_PARAM].getValue()) * inputs[SLOPE_INPUT].getVoltage() / 10.f, 0.f, 1.f);
  188. float shape = clamp(params[SHAPE_PARAM].getValue() + dsp::cubic(params[SHAPE_CV_PARAM].getValue()) * inputs[SHAPE_INPUT].getVoltage() / 10.f, 0.f, 1.f);
  189. float smoothness = clamp(params[SMOOTHNESS_PARAM].getValue() + dsp::cubic(params[SMOOTHNESS_CV_PARAM].getValue()) * inputs[SMOOTHNESS_INPUT].getVoltage() / 10.f, 0.f, 1.f);
  190. float shift = clamp(params[SHIFT_PARAM].getValue() + dsp::cubic(params[SHIFT_CV_PARAM].getValue()) * inputs[SHIFT_INPUT].getVoltage() / 10.f, 0.f, 1.f);
  191. if (output_mode != previous_output_mode) {
  192. poly_slope_generator.Reset();
  193. previous_output_mode = output_mode;
  194. }
  195. // Render generator
  196. poly_slope_generator.Render(
  197. ramp_mode,
  198. output_mode,
  199. range_mode,
  200. frequency,
  201. slope,
  202. shape,
  203. smoothness,
  204. shift,
  205. trig_flags,
  206. !inputs[TRIG_INPUT].isConnected() && inputs[CLOCK_INPUT].isConnected() ? ramp : NULL,
  207. out,
  208. tides2::kBlockSize);
  209. // Set lights
  210. lights[RANGE_LIGHT + 0].value = (range == 0 || range == 1);
  211. lights[RANGE_LIGHT + 1].value = (range == 1 || range == 2);
  212. lights[OUTPUT_MODE_LIGHT + 0].value = (output_mode == tides2::OUTPUT_MODE_AMPLITUDE || output_mode == tides2::OUTPUT_MODE_SLOPE_PHASE);
  213. lights[OUTPUT_MODE_LIGHT + 1].value = (output_mode == tides2::OUTPUT_MODE_FREQUENCY || output_mode == tides2::OUTPUT_MODE_SLOPE_PHASE);
  214. lights[RAMP_MODE_LIGHT + 0].value = (ramp_mode == tides2::RAMP_MODE_AD || ramp_mode == tides2::RAMP_MODE_LOOPING);
  215. lights[RAMP_MODE_LIGHT + 1].value = (ramp_mode == tides2::RAMP_MODE_AR || ramp_mode == tides2::RAMP_MODE_LOOPING);
  216. }
  217. // Outputs
  218. for (int i = 0; i < 4; i++) {
  219. float value = out[frame].channel[i];
  220. outputs[OUT_OUTPUTS + i].setVoltage(value);
  221. lights[OUTPUT_LIGHTS + i].setSmoothBrightness(value, args.sampleTime);
  222. }
  223. }
  224. };
  225. struct Tides2Widget : ModuleWidget {
  226. Tides2Widget(Tides2* module) {
  227. setModule(module);
  228. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Tides2.svg")));
  229. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  230. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  231. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  232. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  233. addParam(createParamCentered<TL1105>(mm2px(Vec(7.425, 16.15)), module, Tides2::RANGE_PARAM));
  234. addParam(createParamCentered<TL1105>(mm2px(Vec(63.325, 16.15)), module, Tides2::MODE_PARAM));
  235. addParam(createParamCentered<Rogan3PSWhite>(mm2px(Vec(16.325, 33.449)), module, Tides2::FREQUENCY_PARAM));
  236. addParam(createParamCentered<Rogan3PSWhite>(mm2px(Vec(54.425, 33.449)), module, Tides2::SHAPE_PARAM));
  237. addParam(createParamCentered<TL1105>(mm2px(Vec(35.375, 38.699)), module, Tides2::RAMP_PARAM));
  238. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(35.375, 55.549)), module, Tides2::SMOOTHNESS_PARAM));
  239. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(11.575, 60.599)), module, Tides2::SLOPE_PARAM));
  240. addParam(createParamCentered<Rogan1PSWhite>(mm2px(Vec(59.175, 60.599)), module, Tides2::SHIFT_PARAM));
  241. addParam(createParamCentered<Trimpot>(mm2px(Vec(9.276, 80.599)), module, Tides2::SLOPE_CV_PARAM));
  242. addParam(createParamCentered<Trimpot>(mm2px(Vec(22.324, 80.599)), module, Tides2::FREQUENCY_CV_PARAM));
  243. addParam(createParamCentered<Trimpot>(mm2px(Vec(35.375, 80.599)), module, Tides2::SMOOTHNESS_CV_PARAM));
  244. addParam(createParamCentered<Trimpot>(mm2px(Vec(48.425, 80.599)), module, Tides2::SHAPE_CV_PARAM));
  245. addParam(createParamCentered<Trimpot>(mm2px(Vec(61.475, 80.599)), module, Tides2::SHIFT_CV_PARAM));
  246. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.775, 96.499)), module, Tides2::SLOPE_INPUT));
  247. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.225, 96.499)), module, Tides2::FREQUENCY_INPUT));
  248. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(29.675, 96.499)), module, Tides2::V_OCT_INPUT));
  249. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(41.125, 96.499)), module, Tides2::SMOOTHNESS_INPUT));
  250. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.575, 96.499)), module, Tides2::SHAPE_INPUT));
  251. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(64.025, 96.499)), module, Tides2::SHIFT_INPUT));
  252. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.775, 111.099)), module, Tides2::TRIG_INPUT));
  253. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.225, 111.099)), module, Tides2::CLOCK_INPUT));
  254. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(29.675, 111.099)), module, Tides2::OUT_OUTPUTS + 0));
  255. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(41.125, 111.099)), module, Tides2::OUT_OUTPUTS + 1));
  256. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(52.575, 111.099)), module, Tides2::OUT_OUTPUTS + 2));
  257. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(64.025, 111.099)), module, Tides2::OUT_OUTPUTS + 3));
  258. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(13.776, 16.149)), module, Tides2::RANGE_LIGHT));
  259. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(56.975, 16.149)), module, Tides2::OUTPUT_MODE_LIGHT));
  260. addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(35.375, 33.449)), module, Tides2::RAMP_MODE_LIGHT));
  261. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(26.174, 104.749)), module, Tides2::OUTPUT_LIGHTS + 0));
  262. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(37.625, 104.749)), module, Tides2::OUTPUT_LIGHTS + 1));
  263. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(49.075, 104.749)), module, Tides2::OUTPUT_LIGHTS + 2));
  264. addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(60.525, 104.749)), module, Tides2::OUTPUT_LIGHTS + 3));
  265. }
  266. };
  267. Model* modelTides2 = createModel<Tides2, Tides2Widget>("Tides2");