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.

502 lines
14KB

  1. /*
  2. Copyright (c) 2018 bsp
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in all
  10. copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17. SOFTWARE.
  18. */
  19. #include <math.h>
  20. #include <stdlib.h> // memset
  21. #include "bsp.hpp"
  22. namespace rack_plugin_bsp {
  23. typedef union fi_u {
  24. float f;
  25. unsigned int u;
  26. int s;
  27. } fi_t;
  28. // struct TrigButton : CKD6 {
  29. // struct TrigButton : TL1105 {
  30. struct TrigButton : LEDButton {
  31. };
  32. struct NullButton : SVGSwitch, ToggleSwitch {
  33. NullButton() {
  34. addFrame(SVG::load(assetPlugin("res/null.svg")));
  35. addFrame(SVG::load(assetPlugin("res/null.svg")));
  36. }
  37. };
  38. struct Scanner : Module {
  39. static const uint32_t MAX_INPUTS = 16u;
  40. enum ParamIds {
  41. POSITION_PARAM,
  42. MOD_POSITION_AMOUNT_PARAM,
  43. SHAPE_PARAM, // sin..tri..square
  44. WIDTH_PARAM,
  45. TABLE_TYPE_PARAM,
  46. OUT_WINDOW_SHAPE_PARAM,
  47. OUT_WINDOW_OFFSET_SWITCH_PARAM,
  48. RANDOM_TRIG_PARAM,
  49. RANDOM_ENABLE_PARAM,
  50. RANDOM_SEED_PARAM,
  51. NUM_PARAMS
  52. };
  53. enum InputIds {
  54. MIX_1_INPUT,
  55. MIX_2_INPUT,
  56. MIX_3_INPUT,
  57. MIX_4_INPUT,
  58. MIX_5_INPUT,
  59. MIX_6_INPUT,
  60. MIX_7_INPUT,
  61. MIX_8_INPUT,
  62. MIX_9_INPUT,
  63. MIX_10_INPUT,
  64. MIX_11_INPUT,
  65. MIX_12_INPUT,
  66. MIX_13_INPUT,
  67. MIX_14_INPUT,
  68. MIX_15_INPUT,
  69. MIX_16_INPUT,
  70. MOD_POSITION_INPUT,
  71. NUM_INPUTS
  72. };
  73. enum OutputIds {
  74. MIX_OUTPUT,
  75. WIN_OUTPUT,
  76. NUM_OUTPUTS
  77. };
  78. enum LightIds {
  79. MIX_1_LIGHT,
  80. MIX_2_LIGHT,
  81. MIX_3_LIGHT,
  82. MIX_4_LIGHT,
  83. MIX_5_LIGHT,
  84. MIX_6_LIGHT,
  85. MIX_7_LIGHT,
  86. MIX_8_LIGHT,
  87. MIX_9_LIGHT,
  88. MIX_10_LIGHT,
  89. MIX_11_LIGHT,
  90. MIX_12_LIGHT,
  91. MIX_13_LIGHT,
  92. MIX_14_LIGHT,
  93. MIX_15_LIGHT,
  94. MIX_16_LIGHT,
  95. NUM_LIGHTS
  96. };
  97. #define MIX_LUT_SIZE (4096)
  98. // (note) the table is actually symmetric (center = LUT_SIZE/2)
  99. float mix_lut[MIX_LUT_SIZE + 1];
  100. float last_mix_shape;
  101. float last_out_shape;
  102. static const uint32_t OUT_BUFFER_SIZE = 32u;
  103. static const uint32_t OUT_BUFFER_MASK = (OUT_BUFFER_SIZE - 1u);
  104. float out_lut[OUT_BUFFER_SIZE + 1];
  105. float out_buffer[OUT_BUFFER_SIZE];
  106. // (note) the table is actually symmetric (center = LUT_SIZE/2)
  107. uint32_t out_buffer_idx;
  108. uint32_t input_shuffle_lut[MAX_INPUTS];
  109. fi_t last_input_shuffle_seed;
  110. fi_t tmp_seed;
  111. float last_rand_enable;
  112. uint32_t last_num_active_inputs;
  113. Scanner() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  114. last_mix_shape = -999.f;
  115. last_out_shape = -999.f;
  116. tmp_seed.u = 0u;
  117. last_num_active_inputs = 0u;
  118. last_rand_enable = -999.f;
  119. memset((void*)out_buffer, 0, sizeof(out_buffer));
  120. out_buffer_idx = 0u;
  121. }
  122. void calcLUT (float *_lut, const uint32_t _lutSize, const float _shape);
  123. void calcMixLUT (void);
  124. void calcOutLUT (void);
  125. uint32_t fastRand (void);
  126. void calcInputShuffleLUT (uint32_t _numActiveInputs);
  127. void step() override;
  128. };
  129. void Scanner::calcLUT(float *_lut, const uint32_t _lutSize, const float _shape) {
  130. // printf("xxx Scanner::calcMixLUT: shape=%f\n", _shape);
  131. float x = 0.0f;
  132. float stepX = (1.0f / _lutSize);
  133. for(uint32_t i = 0u ; i < _lutSize; i++)
  134. {
  135. float cy = 0.0f;
  136. float triY = x*2;
  137. if(triY > 1.0f)
  138. triY = (2.0f - triY);
  139. float expY = sinf(x*3.14159265359f/*PI*/);
  140. expY = powf(expY, powf(1.0f - _shape, 9.0f)*64 + 1.0f);
  141. float rectY = triY;
  142. // rectY = (triY < 0.5) ? 0.0f : 1.0f;
  143. rectY = powf(triY * 2.0f, powf((_shape - 0.75f)*4, 20) * 400.0f + 1.0f);
  144. if(rectY > 1.0f)
  145. rectY = 1.0f;
  146. if(_shape < 0.5f)
  147. {
  148. cy = expY;
  149. }
  150. else if( (_shape >= 0.5f) && (_shape < 0.75f))
  151. {
  152. float t = (_shape -0.5f) * 4.0f;
  153. cy = expY + (triY - expY) * t;
  154. }
  155. else if(_shape >= 0.75f)
  156. {
  157. float t = (_shape - 0.75f) * 4.0f;
  158. cy = triY + (rectY - triY) * t;
  159. }
  160. // printf("xxx mix_lut[%d] = %f triY=%f\n", i, cy, triY);
  161. _lut[i] = cy;
  162. x += stepX;
  163. }
  164. _lut[_lutSize] = _lut[0];
  165. }
  166. void Scanner::calcMixLUT(void) {
  167. calcLUT(mix_lut, MIX_LUT_SIZE, last_mix_shape);
  168. }
  169. void Scanner::calcOutLUT(void) {
  170. calcLUT(out_lut, OUT_BUFFER_SIZE, last_out_shape);
  171. float sum = 0.0f;
  172. for(uint32_t i = 0u; i < OUT_BUFFER_SIZE; i++)
  173. sum += out_lut[i];
  174. float scl = 1.0f / sum;
  175. for(uint32_t i = 0u; i < OUT_BUFFER_SIZE; i++)
  176. out_lut[i] *= scl;
  177. }
  178. uint32_t Scanner::fastRand(void) {
  179. tmp_seed.u *= 16807u;
  180. printf("xxx fastRand()=%u\n", tmp_seed.u);
  181. return tmp_seed.u >> 10;
  182. }
  183. void Scanner::calcInputShuffleLUT(uint32_t _numActiveInputs) {
  184. printf("xxx Scanner::calcInputShuffleLUT(numActiveInputs=%u)\n", _numActiveInputs);
  185. tmp_seed.f = params[RANDOM_SEED_PARAM].value;
  186. tmp_seed.u &= 0xFFffFFu;
  187. tmp_seed.u += (~tmp_seed.u) & 1u;
  188. if(params[RANDOM_ENABLE_PARAM].value >= 0.5f)
  189. {
  190. for(uint32_t i = 0u; i < _numActiveInputs; i++)
  191. {
  192. // (note) there are other "random" functions that produce non-repeating number sequences
  193. // but this one is good enough (usually <8 iterations to generate 4 unique random values)
  194. bool bDuplicate;
  195. do
  196. {
  197. input_shuffle_lut[i] = fastRand() % _numActiveInputs;
  198. bDuplicate = false;
  199. for(uint32_t j = 0u; j < i; j++)
  200. {
  201. if(input_shuffle_lut[j] == input_shuffle_lut[i])
  202. {
  203. bDuplicate = true;
  204. break;
  205. }
  206. }
  207. }
  208. while(bDuplicate);
  209. }
  210. }
  211. else
  212. {
  213. for(uint32_t i = 0u; i < _numActiveInputs; i++)
  214. {
  215. input_shuffle_lut[i] = i;
  216. }
  217. }
  218. }
  219. void Scanner::step() {
  220. if(params[SHAPE_PARAM].value != last_mix_shape)
  221. {
  222. last_mix_shape = params[SHAPE_PARAM].value;
  223. calcMixLUT();
  224. }
  225. int numInputs = 0;
  226. int inputIdx[16];
  227. float outWeights[16];
  228. for(int i = 0; i < 16; i++)
  229. {
  230. if(inputs[MIX_1_INPUT + i].active)
  231. {
  232. inputIdx[numInputs] = i;
  233. outWeights[numInputs] = 0.0f;
  234. numInputs++;
  235. }
  236. else
  237. {
  238. lights[MIX_1_LIGHT + i].setBrightnessSmooth(0.0f);
  239. }
  240. }
  241. if(params[RANDOM_TRIG_PARAM].value >= 0.5f)
  242. {
  243. // (todo) don't handle UI button in the audio thread
  244. params[RANDOM_TRIG_PARAM].value = 0.0f;
  245. fi_t r; r.s = rand();
  246. params[RANDOM_SEED_PARAM].value = r.f;
  247. }
  248. if((last_num_active_inputs != numInputs) ||
  249. (last_input_shuffle_seed.f != params[RANDOM_SEED_PARAM].value) ||
  250. (last_rand_enable != params[RANDOM_ENABLE_PARAM].value)
  251. )
  252. {
  253. last_num_active_inputs = numInputs;
  254. last_input_shuffle_seed.f = params[RANDOM_SEED_PARAM].value;
  255. last_rand_enable = params[RANDOM_ENABLE_PARAM].value;
  256. calcInputShuffleLUT(numInputs);
  257. }
  258. float mixOut = 0.0f;
  259. static int xxx = 0;
  260. float width = 1.0f + (1.0f - params[WIDTH_PARAM].value) * 3.0f;
  261. if(numInputs > 0)
  262. {
  263. float pos = -params[POSITION_PARAM].value + 0.5f - (inputs[MOD_POSITION_INPUT].value * (1.0f/5.0f))*params[MOD_POSITION_AMOUNT_PARAM].value;
  264. float posStep = 1.0f / numInputs;
  265. float xStep = float(MIX_LUT_SIZE) / numInputs;
  266. float outWSum = 0.0f;
  267. for(int i = 0; i < numInputs; i++)
  268. {
  269. if(pos < 0.0f)
  270. pos += 1.0f;
  271. else if(pos >= 1.0f)
  272. pos -= 1.0f;
  273. float w;
  274. float posF = ((pos - 0.5f) * width);
  275. if((posF > -0.5f) && (posF < 0.5f))
  276. {
  277. posF += 0.5f;
  278. posF *= MIX_LUT_SIZE;
  279. int posI = int(posF);
  280. float posFrac = posF - posI;
  281. w = mix_lut[posI] + (mix_lut[posI + 1] - mix_lut[posI]) * posFrac;
  282. }
  283. else
  284. {
  285. w = 0.0f;
  286. }
  287. #if 0
  288. if(0 == (xxx & 32767))
  289. {
  290. printf("xxx i=%d pos=%f w=%f posStep=%f\n", i, pos, w, posStep);
  291. }
  292. #endif
  293. outWeights[i] = w;
  294. outWSum += w;
  295. pos += posStep;
  296. }
  297. float outWScale = (outWSum > 0.0f) ? (1.0f / outWSum) : 0.0f;
  298. for(int i = 0; i < numInputs; i++)
  299. {
  300. int portIdx = inputIdx[input_shuffle_lut[i]];
  301. lights[MIX_1_LIGHT + portIdx].setBrightnessSmooth(outWeights[i]);
  302. mixOut += inputs[MIX_1_INPUT + portIdx].value * outWeights[i] * outWScale;
  303. }
  304. }
  305. float winOut = 0.0f;
  306. if(outputs[WIN_OUTPUT].active)
  307. {
  308. if(params[OUT_WINDOW_SHAPE_PARAM].value != last_out_shape)
  309. {
  310. last_out_shape = params[OUT_WINDOW_SHAPE_PARAM].value;
  311. calcOutLUT();
  312. }
  313. out_buffer[out_buffer_idx] = mixOut;
  314. uint32_t j;
  315. bool bOffsetSw = (params[Scanner::OUT_WINDOW_OFFSET_SWITCH_PARAM].value >= 0.5f);
  316. if(bOffsetSw)
  317. {
  318. // (note) this used to be a bug in the first implementation but it turned out
  319. // that this produces some nice hihat / cymbal sounds so I left it
  320. // in as a switchable parameter
  321. j = (out_buffer_idx - (OUT_BUFFER_SIZE>>1)) & OUT_BUFFER_MASK;
  322. for(uint32_t i = 0u; i < OUT_BUFFER_SIZE; i++)
  323. {
  324. winOut += out_buffer[j] * out_lut[i];
  325. j = (j + 1u) & OUT_BUFFER_MASK;
  326. }
  327. }
  328. else
  329. {
  330. j = out_buffer_idx;
  331. for(uint32_t i = 0u; i < OUT_BUFFER_SIZE; i++)
  332. {
  333. j = (j - 1u) & OUT_BUFFER_MASK;
  334. winOut += out_buffer[j] * out_lut[i];
  335. }
  336. }
  337. out_buffer_idx = (out_buffer_idx + 1u) & OUT_BUFFER_MASK;
  338. }
  339. #if 1
  340. if(0 == (++xxx & 32767))
  341. {
  342. // printf("xxx numInputs=%d\n", numInputs);
  343. // printf("xxx mix_lut[2048]=%f\n", mix_lut[MIX_LUT_SIZE/2]);
  344. // printf("xxx outWeights=[%f; %f; %f; %f]\n", outWeights[0], outWeights[1], outWeights[2], outWeights[3]);
  345. }
  346. #endif
  347. outputs[MIX_OUTPUT].value = mixOut;
  348. outputs[WIN_OUTPUT].value = winOut;
  349. }
  350. struct ScannerWidget : ModuleWidget {
  351. ScannerWidget(Scanner *module);
  352. };
  353. ScannerWidget::ScannerWidget(Scanner *module) : ModuleWidget(module) {
  354. setPanel(SVG::load(assetPlugin(plugin, "res/Scanner.svg")));
  355. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  356. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  357. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  358. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  359. // Audio input ports
  360. {
  361. float cy = 20.0f;
  362. int k = 0;
  363. for(int y = 0; y < 4; y++)
  364. {
  365. float cx = 3.6f;
  366. for(int x = 0; x < 4; x++)
  367. {
  368. addInput(Port::create<PJ301MPort>(mm2px(Vec(cx, cy)), Port::INPUT, module, Scanner::MIX_1_INPUT + k));
  369. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(mm2px(Vec(cx+3.1f, cy-3.5f)), module, Scanner::MIX_1_LIGHT + k));
  370. cx += 12.0f;
  371. k++;
  372. }
  373. cy += 15.0f;
  374. }
  375. }
  376. #define STX 35
  377. #define STY 40
  378. float cx = 9.0f;
  379. float cy = 248.0f;
  380. addParam(ParamWidget::create<RoundBlackKnob>(Vec(cx, cy), module, Scanner::POSITION_PARAM, -1.0f, 1.0f, 0.0f));
  381. cx += STX;
  382. addParam(ParamWidget::create<RoundBlackKnob>(Vec(cx, cy), module, Scanner::MOD_POSITION_AMOUNT_PARAM, -1.0f, 1.0f, 0.0f));
  383. addInput(Port::create<PJ301MPort>(Vec(cx+2.3f, cy + 37.0f), Port::INPUT, module, Scanner::MOD_POSITION_INPUT));
  384. cx += STX;
  385. addParam(ParamWidget::create<RoundBlackKnob>(Vec(cx, cy), module, Scanner::SHAPE_PARAM, 0.0f, 1.0f, 0.45f));
  386. cx += STX;
  387. addParam(ParamWidget::create<RoundBlackKnob>(Vec(cx, cy), module, Scanner::WIDTH_PARAM, 0.0f, 1.0f, 1.0f));
  388. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(27, box.size.y - 60), module, Scanner::OUT_WINDOW_SHAPE_PARAM, 0.0f, 1.0f, 0.34f));
  389. addParam(ParamWidget::create<CKSS>(Vec(9, box.size.y-58), module, Scanner::OUT_WINDOW_OFFSET_SWITCH_PARAM, 0.0f, 1.0f, 0.0f));
  390. cy = 286.0f;
  391. addParam(ParamWidget::create<TrigButton>(Vec(box.size.x - 45, cy+2.0f), module, Scanner::RANDOM_TRIG_PARAM, 0.0f, 1.0f, 0.0f));
  392. addParam(ParamWidget::create<CKSS>(Vec(box.size.x - 25, cy), module, Scanner::RANDOM_ENABLE_PARAM, 0.0f, 1.0f, 0.0f));
  393. addParam(ParamWidget::create<NullButton>(Vec(box.size.x - 70, cy-30), module, Scanner::RANDOM_SEED_PARAM, -INFINITY, INFINITY, 0.0f));
  394. addOutput(Port::create<PJ301MPort>(Vec(box.size.x - 40, 320), Port::OUTPUT, module, Scanner::MIX_OUTPUT));
  395. addOutput(Port::create<PJ301MPort>(Vec(box.size.x - 90, 320), Port::OUTPUT, module, Scanner::WIN_OUTPUT));
  396. }
  397. } // namespace rack_plugin_bsp
  398. using namespace rack_plugin_bsp;
  399. RACK_PLUGIN_MODEL_INIT(bsp, Scanner) {
  400. Model *modelScanner = Model::create<Scanner, ScannerWidget>("bsp", "Scanner", "Scanner", MIXER_TAG);
  401. return modelScanner;
  402. }