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.

608 lines
18KB

  1. #include <array>
  2. #include "Southpole.hpp"
  3. #include "dsp/digital.hpp"
  4. #include "Bjorklund.hpp"
  5. namespace rack_plugin_Southpole {
  6. struct Sns : Module {
  7. enum ParamIds {
  8. K_PARAM,
  9. L_PARAM,
  10. R_PARAM,
  11. S_PARAM,
  12. P_PARAM,
  13. A_PARAM,
  14. CLK_PARAM,
  15. TRIG_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. K_INPUT,
  20. L_INPUT,
  21. R_INPUT,
  22. S_INPUT,
  23. A_INPUT,
  24. P_INPUT,
  25. CLK_INPUT,
  26. RESET_INPUT,
  27. NUM_INPUTS
  28. };
  29. enum OutputIds {
  30. GATE_OUTPUT,
  31. ACCENT_OUTPUT,
  32. CLK_OUTPUT,
  33. RESET_OUTPUT,
  34. NUM_OUTPUTS
  35. };
  36. enum LightIds {
  37. NUM_LIGHTS
  38. };
  39. Bjorklund euclid;
  40. Bjorklund euclid2;
  41. #define MAXLEN 32
  42. // calculated sequence/accents
  43. std::vector<bool> seq0;
  44. std::vector<bool> acc0;
  45. //padded+rotated+distributed
  46. std::array<bool, MAXLEN> sequence;
  47. std::array<bool, MAXLEN> accents;
  48. bool calculate;
  49. bool from_reset;
  50. enum patternStyle {
  51. EUCLIDEAN_PATTERN,
  52. RANDOM_PATTERN,
  53. FIBONACCI_PATTERN,
  54. LINEAR_PATTERN,
  55. CANTOR_PATTERN
  56. } style = EUCLIDEAN_PATTERN;
  57. unsigned int par_k=4; // fill
  58. unsigned int par_l=10; // pattern length
  59. unsigned int par_r=1; // rotation
  60. unsigned int par_p=1; // padding
  61. unsigned int par_s=1; // shift
  62. unsigned int par_a=3; // accent
  63. unsigned int par_last; // checksum
  64. unsigned int par_k_last;
  65. unsigned int par_l_last;
  66. unsigned int par_a_last;
  67. SchmittTrigger clockTrigger;
  68. SchmittTrigger resetTrigger;
  69. PulseGenerator gatePulse;
  70. PulseGenerator accentPulse;
  71. bool gateOn;
  72. bool accOn;
  73. enum gateModes {
  74. TRIGGER_MODE,
  75. GATE_MODE,
  76. TURING_MODE
  77. } gateMode = TRIGGER_MODE;
  78. unsigned int currentStep = 0;
  79. unsigned int turing = 0;
  80. Sns() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  81. sequence.fill(0);
  82. accents.fill(0);
  83. reset();
  84. }
  85. void step() override;
  86. void reset() override;
  87. unsigned int fib(unsigned int n){
  88. return (n < 2) ? n : fib(n - 1) + fib(n - 2);
  89. }
  90. json_t *toJson() override {
  91. json_t *rootJ = json_object();
  92. json_object_set_new(rootJ, "mode", json_integer((int) gateMode));
  93. json_object_set_new(rootJ, "style", json_integer((int) style));
  94. return rootJ;
  95. }
  96. void fromJson(json_t *rootJ) override {
  97. json_t *modeJ = json_object_get(rootJ, "mode");
  98. if (modeJ) {
  99. gateMode = (gateModes) json_integer_value(modeJ);
  100. }
  101. json_t *styleJ = json_object_get(rootJ, "style");
  102. if (styleJ) {
  103. style = (patternStyle) json_integer_value(styleJ);
  104. }
  105. }
  106. };
  107. void Sns::reset() {
  108. if ( par_l_last != par_l ) {
  109. std::fill(seq0.begin(), seq0.end(), 0);
  110. seq0.resize(par_l+par_p);
  111. }
  112. if ( par_k_last != par_k ) {
  113. std::fill(acc0.begin(), acc0.end(), 0);
  114. acc0.resize(par_k);
  115. }
  116. if ( style == RANDOM_PATTERN ) {
  117. if ( par_l_last != par_l || par_k_last != par_k) {
  118. int n = 0;
  119. seq0.resize(par_l);
  120. std::fill(seq0.begin(), seq0.end(), 0);
  121. unsigned int f = 0;
  122. while ( f < par_k ) {
  123. if ( randomUniform() < (float)par_k/(float)par_l ) {
  124. seq0.at(n % par_l) = 1;
  125. f++;
  126. }
  127. n++;
  128. }
  129. }
  130. if ( par_a && (par_a_last != par_a || par_k_last != par_k) ) {
  131. int n = 0;
  132. acc0.resize(par_k);
  133. std::fill(acc0.begin(), acc0.end(), 0);
  134. unsigned int nacc = 0;
  135. while ( nacc < par_a ) {
  136. if ( randomUniform() < (float)par_a/(float)par_k ) {
  137. acc0.at(n % par_k) = 1;
  138. nacc++;
  139. }
  140. n++;
  141. }
  142. }
  143. }
  144. if ( style == FIBONACCI_PATTERN ) {
  145. seq0.resize(par_l);
  146. std::fill(seq0.begin(), seq0.end(), 0);
  147. for ( unsigned int k = 0; k < par_k; k++ ) {
  148. seq0.at(fib(k) % par_l) = 1;
  149. }
  150. acc0.resize(par_k);
  151. std::fill(acc0.begin(), acc0.end(), 0);
  152. for ( unsigned int a = 0; a < par_a; a++ ) {
  153. acc0.at(fib(a) % par_k) = 1;
  154. }
  155. }
  156. // if ( style == CANTOR_PATTERN ) {
  157. if ( style == LINEAR_PATTERN ) {
  158. seq0.resize(par_l);
  159. std::fill(seq0.begin(), seq0.end(), 0);
  160. for ( unsigned int k = 0; k < par_k; k++ ) {
  161. seq0.at( par_l*k/par_k ) = 1;
  162. }
  163. acc0.resize(par_k);
  164. std::fill(acc0.begin(), acc0.end(), 0);
  165. for ( unsigned int a = 0; a < par_a; a++ ) {
  166. acc0.at( par_k*a/par_a ) = 1;
  167. }
  168. }
  169. if ( style == EUCLIDEAN_PATTERN ) {
  170. euclid.reset();
  171. euclid.init(par_l,par_k);
  172. euclid.iter();
  173. euclid2.reset();
  174. if (par_a>0) {
  175. euclid2.init(par_k, par_a);
  176. euclid2.iter();
  177. }
  178. seq0 = euclid.sequence;
  179. acc0 = euclid2.sequence;
  180. }
  181. // pad sequence
  182. //if (seq0.size() != par_l+par_p) seq0.resize(par_l+par_p);
  183. //for (unsigned int i=par_l; i<par_p; i++) { seq0.at(i) = 0; }
  184. //for (unsigned int i = 0; i != seq0.size(); i++) { std::cout << seq0[i]; }
  185. //std::cout << '\n';
  186. //for (unsigned int i = 0; i != acc0.size(); i++) { std::cout << acc0[i]; }
  187. //std::cout << '\n';
  188. // distribute accents on sequence
  189. unsigned int j = par_k-par_s;
  190. for (unsigned int i = 0; i != seq0.size(); i++) {
  191. unsigned int idx = (i + par_r)%(par_l + par_p);
  192. sequence[idx] = seq0.at(i);
  193. accents[idx] = 0;
  194. if (par_a && seq0.at(i)) {
  195. accents[idx] = acc0.at( j % par_k );
  196. j++;
  197. }
  198. }
  199. //for (unsigned int i = 0; i != sequence.size(); i++) { std::cout << sequence[i]; }
  200. //std::cout << '\n';
  201. //for (unsigned int i = 0; i != accents.size(); i++) { std::cout << accents[i]; }
  202. //std::cout << '\n';
  203. calculate = false;
  204. from_reset = true;
  205. }
  206. void Sns::step() {
  207. bool nextStep = false;
  208. // reset sequence
  209. if (inputs[RESET_INPUT].active) {
  210. if (resetTrigger.process(inputs[RESET_INPUT].value)) {
  211. currentStep = par_l + par_p;
  212. }
  213. outputs[RESET_OUTPUT].value = inputs[RESET_INPUT].value;
  214. }
  215. if (inputs[CLK_INPUT].active) {
  216. if (clockTrigger.process(inputs[CLK_INPUT].value)) {
  217. nextStep = true;
  218. }
  219. outputs[CLK_OUTPUT].value = inputs[CLK_INPUT].value;
  220. }
  221. if (nextStep) {
  222. currentStep++;
  223. if (currentStep >= par_l + par_p) {
  224. currentStep = 0;
  225. }
  226. if ( gateMode == TURING_MODE ) {
  227. turing = 0;
  228. for (unsigned int i=0; i<par_l; i++) {
  229. turing |= sequence[(currentStep + i) % par_l+par_p];
  230. turing <<= 1;
  231. }
  232. } else {
  233. gateOn = false;
  234. if (sequence[currentStep] ) {
  235. gatePulse.trigger(1e-3);
  236. if ( gateMode == GATE_MODE ) {
  237. gateOn = true;
  238. }
  239. }
  240. }
  241. accOn = false;
  242. if (par_a && accents.at( currentStep )) {
  243. accentPulse.trigger(1e-3);
  244. if ( gateMode == GATE_MODE ) {
  245. accOn = true;
  246. }
  247. }
  248. }
  249. bool gpulse = gatePulse.process(1.0 / engineGetSampleRate());
  250. bool apulse = accentPulse.process(1.0 / engineGetSampleRate());
  251. if ( gateMode == TURING_MODE ) {
  252. outputs[GATE_OUTPUT].value = 10.0*(turing / pow(2.,par_l) - 1.);
  253. } else {
  254. outputs[GATE_OUTPUT].value = gateOn | gpulse ? 10.0 : 0.0;
  255. }
  256. outputs[ACCENT_OUTPUT].value = accOn | apulse ? 10.0 : 0.0;
  257. par_l = (unsigned int) ( 1. + 15. * clamp( params[L_PARAM].value + inputs[L_INPUT].normalize(0.) / 9., 0.0f, 1.0f));
  258. par_p = (unsigned int) (32. - par_l) * clamp( params[P_PARAM].value + inputs[P_INPUT].normalize(0.) / 9., 0.0f, 1.0f);
  259. par_r = (unsigned int) (par_l + par_p - 1.) * clamp( params[R_PARAM].value + inputs[R_INPUT].normalize(0.) / 9., 0.0f, 1.0f);
  260. par_k = (unsigned int) ( 1. + (par_l-1.) * clamp( params[K_PARAM].value + inputs[K_INPUT].normalize(0.) / 9., 0.0f, 1.0f));
  261. par_a = (unsigned int) ( par_k ) * clamp( params[A_PARAM].value + inputs[A_INPUT].normalize(0.) / 9., 0.0f, 1.0f);
  262. if (par_a == 0) {
  263. par_s = 0;
  264. } else {
  265. par_s = (unsigned int) ( par_k-1. ) * clamp( params[S_PARAM].value + inputs[S_INPUT].normalize(0.) / 9., 0.0f, 1.0f);
  266. }
  267. // new sequence in case of change to parameters
  268. if (par_l+par_r+par_a+par_k+par_p+par_s != par_last) {
  269. // printf("%d %d",par_k,par_a);
  270. par_last = par_l+par_r+par_a+par_k+par_p+par_s;
  271. calculate = true;
  272. }
  273. }
  274. struct SnsDisplay : TransparentWidget {
  275. Sns *module;
  276. int frame = 0;
  277. std::shared_ptr<Font> font;
  278. float y1;
  279. float yh;
  280. SnsDisplay( float y1_, float yh_ ) {
  281. y1 = y1_;
  282. yh = yh_;
  283. //font = Font::load(assetPlugin(plugin, "res/fonts/Sudo.ttf"));
  284. font = Font::load(assetPlugin(plugin, "res/hdad-segment14-1.002/Segment14.ttf"));
  285. }
  286. void drawPolygon(NVGcontext *vg) {
  287. Rect b = Rect(Vec(2, 2), box.size.minus(Vec(2, 2)));
  288. float cx = 0.5*b.size.x+1;
  289. float cy = 0.5*b.size.y-12;
  290. const float r1 = .45*b.size.x;
  291. const float r2 = .35*b.size.x;
  292. // Circles
  293. nvgBeginPath(vg);
  294. nvgStrokeColor(vg, nvgRGBA(0x7f, 0x00, 0x00, 0xff));
  295. nvgFillColor(vg, nvgRGBA(0xff, 0x00, 0x00, 0xff));
  296. nvgStrokeWidth(vg, 1.);
  297. nvgCircle(vg, cx, cy, r1);
  298. nvgCircle(vg, cx, cy, r2);
  299. nvgStroke(vg);
  300. unsigned len = module->par_l + module->par_p;
  301. nvgStrokeColor(vg, nvgRGBA(0xff, 0x00, 0x00, 0xff));
  302. nvgBeginPath(vg);
  303. bool first = true;
  304. // inactive Step Rings
  305. for (unsigned i = 0; i < len; i++) {
  306. if ( !module->sequence[i] ) {
  307. float r = module->accents[i] ? r1 : r2;
  308. float x = cx + r * cosf(2.*M_PI*i/len-.5*M_PI);
  309. float y = cy + r * sinf(2.*M_PI*i/len-.5*M_PI);
  310. nvgBeginPath(vg);
  311. nvgFillColor(vg, nvgRGBA(0x30, 0x10, 0x10, 0xff));
  312. nvgStrokeWidth(vg, 1.);
  313. nvgStrokeColor(vg, nvgRGBA(0x7f, 0x00, 0x00, 0xff));
  314. nvgCircle(vg, x, y, 3.);
  315. nvgFill(vg);
  316. nvgStroke(vg);
  317. }
  318. }
  319. // Path
  320. nvgBeginPath(vg);
  321. nvgStrokeColor(vg, nvgRGBA(0xff, 0x00, 0x00, 0xff));
  322. nvgStrokeWidth(vg, 1.);
  323. for (unsigned int i = 0; i < len; i++) {
  324. if ( module->sequence[i] ) {
  325. float a = i/float(len);
  326. float r = module->accents[i] ? r1 : r2;
  327. float x = cx + r * cosf(2.*M_PI*a-.5*M_PI);
  328. float y = cy + r * sinf(2.*M_PI*a-.5*M_PI);
  329. Vec p(x,y);
  330. if (module->par_k == 1) nvgCircle(vg, x, y, 3.);
  331. if (first) {
  332. nvgMoveTo(vg, p.x, p.y);
  333. first = false;
  334. } else {
  335. nvgLineTo(vg, p.x, p.y);
  336. }
  337. }
  338. }
  339. nvgClosePath(vg);
  340. nvgStroke(vg);
  341. // Active Step Rings
  342. for (unsigned i = 0; i < len; i++) {
  343. if ( module->sequence[i] ) {
  344. float r = module->accents[i] ? r1 : r2;
  345. float x = cx + r * cosf(2.*M_PI*i/len-.5*M_PI);
  346. float y = cy + r * sinf(2.*M_PI*i/len-.5*M_PI);
  347. nvgBeginPath(vg);
  348. nvgFillColor(vg, nvgRGBA(0x30, 0x10, 0x10, 0xff));
  349. nvgStrokeWidth(vg, 1.);
  350. nvgStrokeColor(vg, nvgRGBA(0xff, 0x00, 0x00, 0xff));
  351. nvgCircle(vg, x, y, 3.);
  352. nvgFill(vg);
  353. nvgStroke(vg);
  354. }
  355. }
  356. unsigned int i = module->currentStep;
  357. float r = module->accents[i] ? r1 : r2;
  358. float x = cx + r * cosf(2.*M_PI*i/len-.5*M_PI);
  359. float y = cy + r * sinf(2.*M_PI*i/len-.5*M_PI);
  360. nvgBeginPath(vg);
  361. nvgStrokeColor(vg, nvgRGBA(0xff, 0x00, 0x00, 0xff));
  362. if ( module->sequence[i] ) {
  363. nvgFillColor(vg, nvgRGBA(0xff, 0x00, 0x00, 0xff));
  364. } else {
  365. nvgFillColor(vg, nvgRGBA(0x30, 0x10, 0x10, 0xff));
  366. }
  367. nvgCircle(vg, x, y, 3.);
  368. nvgStrokeWidth(vg, 1.5);
  369. nvgFill(vg);
  370. nvgStroke(vg);
  371. }
  372. void draw(NVGcontext *vg) override {
  373. // i know ... shouldn't be here at all
  374. if (module->calculate) {
  375. module->reset();
  376. module->par_k_last = module->par_k;
  377. module->par_l_last = module->par_l;
  378. module->par_a_last = module->par_a;
  379. }
  380. // Background
  381. NVGcolor backgroundColor = nvgRGB(0x30, 0x10, 0x10);
  382. NVGcolor borderColor = nvgRGB(0xd0, 0xd0, 0xd0);
  383. nvgBeginPath(vg);
  384. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  385. nvgFillColor(vg, backgroundColor);
  386. nvgFill(vg);
  387. nvgStrokeWidth(vg, 1.5);
  388. nvgStrokeColor(vg, borderColor);
  389. nvgStroke(vg);
  390. drawPolygon(vg);
  391. nvgFontSize(vg, 8);
  392. nvgFontFaceId(vg, font->handle);
  393. Vec textPos = Vec(15, 105);
  394. NVGcolor textColor = nvgRGB(0xff, 0x00, 0x00);
  395. nvgFillColor(vg, textColor);
  396. char str[20];
  397. snprintf(str,sizeof(str),"%2d %2d %2d",int(module->par_k),int(module->par_l),int(module->par_r));
  398. nvgText(vg, textPos.x, textPos.y-11, str, NULL);
  399. snprintf(str,sizeof(str),"%2d %2d %2d",int(module->par_p),int(module->par_a),int(module->par_s));
  400. nvgText(vg, textPos.x, textPos.y, str, NULL);
  401. }
  402. };
  403. struct SnsWidget : ModuleWidget {
  404. SnsWidget();
  405. Menu *createContextMenu() override;
  406. SnsWidget(Sns *module) : ModuleWidget(module) {
  407. box.size = Vec(15*6, 380);
  408. {
  409. SVGPanel *panel = new SVGPanel();
  410. panel->box.size = box.size;
  411. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Sns.svg")));
  412. addChild(panel);
  413. }
  414. {
  415. SnsDisplay *display = new SnsDisplay(180,30);
  416. display->module = module;
  417. display->box.pos = Vec( 3., 30);
  418. display->box.size = Vec(box.size.x-6., 110. );
  419. addChild(display);
  420. }
  421. const float y1 = 160;
  422. const float yh = 30;
  423. float x1 = 4.;
  424. float x2 = 4.+30;
  425. float x3 = 4.+60;
  426. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x1, y1 ), module, Sns::K_PARAM, 0., 1., .25));
  427. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x2, y1 ), module, Sns::L_PARAM, 0., 1., 1.));
  428. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x3, y1 ), module, Sns::R_PARAM, 0., 1., 0.));
  429. addInput(Port::create<sp_Port>(Vec(x1, y1+1*yh), Port::INPUT, module, Sns::K_INPUT));
  430. addInput(Port::create<sp_Port>(Vec(x2, y1+1*yh), Port::INPUT, module, Sns::L_INPUT));
  431. addInput(Port::create<sp_Port>(Vec(x3, y1+1*yh), Port::INPUT, module, Sns::R_INPUT));
  432. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x1, y1+2.5*yh), module, Sns::P_PARAM, 0., 1., 0.));
  433. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x2, y1+2.5*yh), module, Sns::A_PARAM, 0., 1., 0.));
  434. addParam(ParamWidget::create<sp_SmallBlackKnob>(Vec(x3, y1+2.5*yh), module, Sns::S_PARAM, 0., 1., 0.));
  435. addInput(Port::create<sp_Port>(Vec(x1, y1+3.5*yh), Port::INPUT, module, Sns::P_INPUT));
  436. addInput(Port::create<sp_Port>(Vec(x2, y1+3.5*yh), Port::INPUT, module, Sns::A_INPUT));
  437. addInput(Port::create<sp_Port>(Vec(x3, y1+3.5*yh), Port::INPUT, module, Sns::S_INPUT));
  438. addInput(Port::create<sp_Port>(Vec(x1, y1+4.65*yh), Port::INPUT, module, Sns::CLK_INPUT));
  439. addInput(Port::create<sp_Port>(Vec(x1, y1+5.4*yh), Port::INPUT, module, Sns::RESET_INPUT));
  440. addOutput(Port::create<sp_Port>(Vec(x3, y1+4.65*yh), Port::OUTPUT, module, Sns::CLK_OUTPUT));
  441. addOutput(Port::create<sp_Port>(Vec(x3, y1+5.4*yh), Port::OUTPUT, module, Sns::RESET_OUTPUT));
  442. addOutput(Port::create<sp_Port>(Vec(x2, y1+4.65*yh), Port::OUTPUT, module, Sns::GATE_OUTPUT));
  443. addOutput(Port::create<sp_Port>(Vec(x2, y1+5.4*yh), Port::OUTPUT, module, Sns::ACCENT_OUTPUT));
  444. //addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(4, 281), module, Sns::CLK_LIGHT));
  445. //addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(4+25, 281), module, Sns::GATE_LIGHT));
  446. //addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(4+50, 281), module, Sns::ACCENT_LIGHT));
  447. }
  448. };
  449. struct SnsGateModeItem : MenuItem {
  450. Sns *sns;
  451. Sns::gateModes gm;
  452. void onAction(EventAction &e) override {
  453. sns->gateMode = gm;
  454. }
  455. void step() override {
  456. rightText = (sns->gateMode == gm) ? "✔" : "";
  457. MenuItem::step();
  458. }
  459. };
  460. struct SnsPatternStyleItem : MenuItem {
  461. Sns *sns;
  462. Sns::patternStyle ps;
  463. void onAction(EventAction &e) override {
  464. sns->style = ps;
  465. sns->reset();
  466. }
  467. void step() override {
  468. rightText = (sns->style == ps) ? "✔" : "";
  469. MenuItem::step();
  470. }
  471. };
  472. Menu *SnsWidget::createContextMenu() {
  473. Sns *sns = dynamic_cast<Sns*>(module);
  474. assert(sns);
  475. Menu *menu = ModuleWidget::createContextMenu();
  476. menu->addChild(construct<MenuLabel>());;
  477. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Gate Mode"));
  478. menu->addChild(construct<SnsGateModeItem>(&MenuItem::text, "Trigger", &SnsGateModeItem::sns, sns, &SnsGateModeItem::gm, Sns::TRIGGER_MODE));
  479. menu->addChild(construct<SnsGateModeItem>(&MenuItem::text, "Gate", &SnsGateModeItem::sns, sns, &SnsGateModeItem::gm, Sns::GATE_MODE));
  480. menu->addChild(construct<SnsGateModeItem>(&MenuItem::text, "Turing", &SnsGateModeItem::sns, sns, &SnsGateModeItem::gm, Sns::TURING_MODE));
  481. menu->addChild(construct<MenuLabel>());;
  482. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Pattern Style"));
  483. menu->addChild(construct<SnsPatternStyleItem>(&MenuItem::text, "Euclid", &SnsPatternStyleItem::sns, sns, &SnsPatternStyleItem::ps, Sns::EUCLIDEAN_PATTERN));
  484. menu->addChild(construct<SnsPatternStyleItem>(&MenuItem::text, "Fibonacci", &SnsPatternStyleItem::sns, sns, &SnsPatternStyleItem::ps, Sns::FIBONACCI_PATTERN));
  485. menu->addChild(construct<SnsPatternStyleItem>(&MenuItem::text, "Random", &SnsPatternStyleItem::sns, sns, &SnsPatternStyleItem::ps, Sns::RANDOM_PATTERN));
  486. //menu->addChild(construct<SnsPatternStyleItem>(&MenuItem::text, "Cantor", &SnsPatternStyleItem::sns, sns, &SnsPatternStyleItem::ps, Sns::CANTOR_PATTERN));
  487. menu->addChild(construct<SnsPatternStyleItem>(&MenuItem::text, "Linear", &SnsPatternStyleItem::sns, sns, &SnsPatternStyleItem::ps, Sns::LINEAR_PATTERN));
  488. return menu;
  489. }
  490. } // namespace rack_plugin_Southpole
  491. using namespace rack_plugin_Southpole;
  492. RACK_PLUGIN_MODEL_INIT(Southpole, Sns) {
  493. Model *modelSns = Model::create<Sns,SnsWidget>( "Southpole", "SNS", "SNS - euclidean sequencer", SEQUENCER_TAG);
  494. return modelSns;
  495. }