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.

654 lines
19KB

  1. #include "global_pre.hpp"
  2. #include <stdio.h>
  3. #include <string>
  4. #include <vector>
  5. #include <unordered_map>
  6. #include "alikins.hpp"
  7. #include "ui.hpp"
  8. #include "global_ui.hpp"
  9. namespace rack_plugin_Alikins {
  10. struct SpecificValue : Module
  11. {
  12. enum ParamIds
  13. {
  14. VALUE1_PARAM,
  15. OCTAVE_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds
  19. {
  20. VALUE1_INPUT,
  21. NUM_INPUTS
  22. };
  23. enum OutputIds
  24. {
  25. VALUE1_OUTPUT,
  26. NUM_OUTPUTS
  27. };
  28. enum LightIds
  29. {
  30. NUM_LIGHTS
  31. };
  32. SpecificValue() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  33. float A440_octave = 4.0f;
  34. void step() override;
  35. // TODO: toJson/fromJson for saving values
  36. float volt_value;
  37. float hz_value;
  38. float period_value;
  39. float cents_value;
  40. };
  41. struct NoteInfo {
  42. std::string name;
  43. float offset_cents;
  44. };
  45. // TODO: mv to header
  46. float A440_VOLTAGE = 4.75f;
  47. int A440_MIDI_NUMBER = 69;
  48. // TODO: support other enharmonic names Db G♭ A♯?
  49. std::vector<std::string> note_name_vec = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
  50. std::unordered_map<std::string, float> gen_note_name_map() {
  51. float volt = -10.0f;
  52. std::string fs = note_name_vec[4];
  53. std::unordered_map<std::string, float> note_name_map;
  54. // FIXME: add a map of note name (including enharmonic) to voltage offset from C
  55. // then just iterate over it for each octave
  56. for (int i = -10; i <= 10; i++)
  57. {
  58. for (int j = 0; j < 12; j++)
  59. {
  60. // debug("oct=%d note=%s volt=%f ", i, note_name_vec[j].c_str(), volt);
  61. note_name_map[stringf("%s%d", note_name_vec[j].c_str(), i)] = volt;
  62. volt += (1.0f / 12.0f);
  63. }
  64. }
  65. return note_name_map;
  66. }
  67. std::unordered_map<std::string, float> note_name_to_volts_map = gen_note_name_map();
  68. // FIXME: can/should be inline
  69. // FIXME: likely should be a NoteInfo type/struct/object
  70. // These are assuming A440 == A4 == 4.75v
  71. float freq_to_cv(float freq, float a440_octave) {
  72. float volts = log2f(freq / 440.0f * powf(2.0f, A440_VOLTAGE)) - a440_octave;
  73. // debug("freq_to_vc freq=%f a440_octave=%f volts=%f A440_voltage=%f", freq, a440_octave, volts, A440_VOLTAGE);
  74. return volts;
  75. }
  76. float cv_to_freq(float volts, float a440_octave) {
  77. float freq = 440.0f / powf(2.0f, A440_VOLTAGE) * powf(2.0f, volts + a440_octave);
  78. // debug("cv_to_freq freq=%f a440_octave=%f volts=%f A440_voltage=%f", freq, a440_octave, volts, A440_VOLTAGE);
  79. return freq;
  80. }
  81. // can return negative
  82. float volts_of_nearest_note(float volts) {
  83. float res = roundf( (volts * 12.0f) ) / 12.0f;
  84. return res;
  85. }
  86. int volts_to_note(float volts) {
  87. int res = abs(static_cast<int>( roundf( (volts * 12.0f) ) ) ) % 12;
  88. // debug("volts_to_note volts=%f res=%d", volts, res);
  89. return res;
  90. }
  91. int volts_to_octave(float volts, float a440_octave) {
  92. // debug("a440_octave=%f", a440_octave);
  93. int octave = floor(volts + a440_octave);
  94. // debug("volts_to_octaves volts=%f, a440_octave=%f, octave=%d", volts, a440_octave, octave);
  95. return octave;
  96. }
  97. float volts_to_note_cents(float volts, float a440_octave) {
  98. float nearest_note = volts_of_nearest_note(volts);
  99. float cent_volt = 1.0f / 12.0f / 100.0f;
  100. float offset_cents = (volts-nearest_note)/cent_volt;
  101. // debug("volts: %f volts_of_nearest: %f volts-volts_nearest: %f offset_cents %f",
  102. // volts, nearest_note, volts-nearest_note, offset_cents);
  103. return offset_cents;
  104. }
  105. int volts_to_midi(float volts, float a440_octave) {
  106. int midi_note = floor(volts * 12.0f + a440_octave) + 21;
  107. return midi_note;
  108. }
  109. void SpecificValue::step()
  110. {
  111. A440_octave = params[OCTAVE_PARAM].value;
  112. if (inputs[VALUE1_INPUT].active) {
  113. params[VALUE1_PARAM].value = inputs[VALUE1_INPUT].value;
  114. }
  115. volt_value = params[VALUE1_PARAM].value;
  116. outputs[VALUE1_OUTPUT].value = volt_value;
  117. }
  118. struct FloatField : TextField
  119. {
  120. float value;
  121. SpecificValue *module;
  122. FloatField(SpecificValue *_module);
  123. void onAction(EventAction &e) override;
  124. void onChange(EventChange &e) override;
  125. float textToVolts(std::string field_text);
  126. std::string voltsToText(float param_volts);
  127. };
  128. FloatField::FloatField(SpecificValue *_module)
  129. {
  130. module = _module;
  131. value = module->params[SpecificValue::VALUE1_PARAM].value;
  132. text = voltsToText(value);
  133. }
  134. // TODO: this is really data stuff, so could be in type/struct/class for the data (volt, freq/hz, period/seconds, note_name)
  135. // and instanced and provided to a generic ValueField widget that has-a data type converter thingy
  136. float FloatField::textToVolts(std::string field_text) {
  137. return atof(field_text.c_str());
  138. }
  139. std::string FloatField::voltsToText(float param_volts){
  140. return stringf("%0.3f", param_volts);
  141. }
  142. void FloatField::onChange(EventChange &e) {
  143. //debug("FloatField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value);
  144. if (this != RACK_PLUGIN_UI_FOCUSED_WIDGET) {
  145. std::string new_text = voltsToText(module->params[SpecificValue::VALUE1_PARAM].value);
  146. setText(new_text);
  147. }
  148. }
  149. void FloatField::onAction(EventAction &e)
  150. {
  151. //debug("FloatField onAction text=%s", text.c_str());
  152. //update text first?
  153. TextField::onAction(e);
  154. float volts = textToVolts(text);
  155. //debug("FloatField setting volts=%f text=%s", volts, text.c_str());
  156. module->params[SpecificValue::VALUE1_PARAM].value = volts;
  157. //debug("FloatField onAction2 text=%s volts=%f module->volt_values=%f",
  158. // text.c_str(), volts, module->volt_value);
  159. }
  160. struct HZFloatField : TextField
  161. {
  162. float value;
  163. SpecificValue *module;
  164. HZFloatField(SpecificValue *_module);
  165. void onChange(EventChange &e) override;
  166. void onAction(EventAction &e) override;
  167. float textToVolts(std::string field_text);
  168. std::string voltsToText(float param_volts);
  169. };
  170. HZFloatField::HZFloatField(SpecificValue *_module)
  171. {
  172. module = _module;
  173. }
  174. float HZFloatField::textToVolts(std::string field_text) {
  175. float freq = strtof(text.c_str(), NULL);
  176. return freq_to_cv(freq, module->A440_octave);
  177. }
  178. std::string HZFloatField::voltsToText(float param_volts){
  179. float freq = cv_to_freq(param_volts, module->A440_octave);
  180. std::string new_text = stringf("%0.*f", freq < 100 ? 4 : 3, freq);
  181. return new_text;
  182. }
  183. void HZFloatField::onChange(EventChange &e) {
  184. //debug("HZFloatField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value);
  185. //TextField::onChange(e);
  186. if (this != RACK_PLUGIN_UI_FOCUSED_WIDGET)
  187. {
  188. std::string new_text = voltsToText(module->params[SpecificValue::VALUE1_PARAM].value);
  189. setText(new_text);
  190. }
  191. }
  192. void HZFloatField::onAction(EventAction &e)
  193. {
  194. //debug("HZFloatField onAction text=%s", text.c_str());
  195. //update text first?
  196. TextField::onAction(e);
  197. float volts = textToVolts(text);
  198. //debug("HZ FloatField onAction about to set VALUE*_PARAM to volts: %f", volts);
  199. module->params[SpecificValue::VALUE1_PARAM].value = volts;
  200. }
  201. struct SecondsFloatField : TextField {
  202. float value;
  203. SpecificValue *module;
  204. SecondsFloatField(SpecificValue *_module);
  205. void onAction(EventAction &e) override;
  206. void onChange(EventChange &e) override;
  207. float textToVolts(std::string field_text);
  208. std::string voltsToText(float param_volts);
  209. };
  210. SecondsFloatField::SecondsFloatField(SpecificValue *_module)
  211. {
  212. module = _module;
  213. }
  214. float SecondsFloatField::textToVolts(std::string field_text) {
  215. float period = strtof(text.c_str(), NULL);
  216. float freq = 1.0f / period;
  217. return freq_to_cv(freq, module->A440_octave);
  218. }
  219. std::string SecondsFloatField::voltsToText(float param_volts){
  220. float period = 1.0f / cv_to_freq(param_volts, module->A440_octave);
  221. std::string new_text = stringf("%0.*f", period < 100 ? 4 : 3, period);
  222. return new_text;
  223. }
  224. void SecondsFloatField::onChange(EventChange &e) {
  225. //debug("SecondsFloatField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value);
  226. //TextField::onChange(e);
  227. if (this != RACK_PLUGIN_UI_FOCUSED_WIDGET)
  228. {
  229. std::string new_text = voltsToText(module->params[SpecificValue::VALUE1_PARAM].value);
  230. setText(new_text);
  231. }
  232. }
  233. void SecondsFloatField::onAction(EventAction &e) {
  234. //debug("SecondsFloatField onAction text=%s", text.c_str());
  235. //update text first?
  236. TextField::onAction(e);
  237. float volts = textToVolts(text);
  238. //debug("SecondsFloatField onAction about to set VALUE*_PARAM to volts: %f", volts);
  239. module->params[SpecificValue::VALUE1_PARAM].value = volts;
  240. }
  241. struct CentsField : TextField {
  242. float value;
  243. SpecificValue *module;
  244. CentsField(SpecificValue *_module);
  245. void onChange(EventChange &e) override;
  246. void onAction(EventAction &e) override;
  247. };
  248. CentsField::CentsField(SpecificValue *_module) {
  249. module = _module;
  250. }
  251. void CentsField::onChange(EventChange &e) {
  252. // debug("CentsField onChange");
  253. float cents = volts_to_note_cents(module->params[SpecificValue::VALUE1_PARAM].value,
  254. module->params[SpecificValue::OCTAVE_PARAM].value);
  255. // debug("CentsField onChange cents: %f", cents);
  256. if (this != RACK_PLUGIN_UI_FOCUSED_WIDGET || fabs(cents) >= 0.50f)
  257. {
  258. float cents = volts_to_note_cents(module->params[SpecificValue::VALUE1_PARAM].value,
  259. module->params[SpecificValue::OCTAVE_PARAM].value);
  260. std::string new_text = stringf("% 0.2f", cents);
  261. setText(new_text);
  262. }
  263. }
  264. void CentsField::onAction(EventAction &e) {
  265. TextField::onAction(e);
  266. float cents = strtof(text.c_str(), NULL);
  267. // figure what to tweak the current volts
  268. float cent_volt = 1.0f / 12.0f / 100.0f;
  269. float delta_volt = cents * cent_volt;
  270. float nearest_note_voltage = volts_of_nearest_note(module->params[SpecificValue::VALUE1_PARAM].value);
  271. //debug("volts: %f nearest_volts: %f", module->params[SpecificValue::VALUE1_PARAM].value, nearest_note_voltage);
  272. //debug("delta_volt: %+f nearest_note_voltage+delta_volt: %f", delta_volt, nearest_note_voltage,
  273. // nearest_note_voltage + delta_volt);
  274. module->params[SpecificValue::VALUE1_PARAM].value = nearest_note_voltage + delta_volt;
  275. }
  276. struct NoteNameField : TextField {
  277. float value;
  278. SpecificValue *module;
  279. NoteNameField(SpecificValue *_module);
  280. void onChange(EventChange &e) override;
  281. void onAction(EventAction &e) override;
  282. };
  283. NoteNameField::NoteNameField(SpecificValue *_module)
  284. {
  285. module = _module;
  286. }
  287. void NoteNameField::onChange(EventChange &e) {
  288. //debug("NoteNameField onChange text=%s param=%f", text.c_str(), module->params[SpecificValue::VALUE1_PARAM].value);
  289. //TextField::onChange(e);
  290. if (this != RACK_PLUGIN_UI_FOCUSED_WIDGET)
  291. {
  292. float cv_volts = module->params[SpecificValue::VALUE1_PARAM].value;
  293. int octave = volts_to_octave(cv_volts, module->params[SpecificValue::OCTAVE_PARAM].value);
  294. int note_number = volts_to_note(cv_volts);
  295. // float semi_cents = volts_to_note_and_cents(cv_volts, module->params[SpecificValue::OCTAVE_PARAM].value);
  296. // note_info = volts_to_note_info(cv_volts, module->params[SpecificValue::OCTAVE_PARAM].value);
  297. // TODO: modf for oct/fract part, need to get +/- cents from chromatic notes
  298. std::string new_text = stringf("%s%d", note_name_vec[note_number].c_str(), octave);
  299. // debug("foo %f bar %f", )
  300. setText(new_text);
  301. }
  302. }
  303. void NoteNameField::onAction(EventAction &e) {
  304. //debug("NoteNameField onAction");
  305. TextField::onAction(e);
  306. // FIXME: Haven't tested but seems like this does a lot.
  307. // FIXME: I suspect just a array of structs with name/freq in it and a linear search makes more sense
  308. // but lets c++ stuff
  309. auto search = note_name_to_volts_map.find(text);
  310. if(search != note_name_to_volts_map.end()) {
  311. /*
  312. debug("note_name_to_volts_map[%s] = %f (%f) %f", text.c_str(),
  313. note_name_to_volts_map[text],
  314. (note_name_to_volts_map[text] - module->A440_octave),
  315. module->A440_octave );
  316. */
  317. module->params[SpecificValue::VALUE1_PARAM].value = note_name_to_volts_map[text] - module->A440_octave;
  318. return;
  319. }
  320. else {
  321. // TODO: change the text color to indicate bogus name?
  322. debug("%s was NOT A VALID note name", text.c_str());
  323. return;
  324. }
  325. }
  326. struct SmallPurpleTrimpot : Trimpot {
  327. SmallPurpleTrimpot();
  328. };
  329. SmallPurpleTrimpot::SmallPurpleTrimpot() : Trimpot() {
  330. setSVG(SVG::load(assetPlugin(plugin, "res/SmallPurpleTrimpot.svg")));
  331. shadow->blurRadius = 0.0;
  332. shadow->opacity = 0.10;
  333. shadow->box.pos = Vec(0.0, box.size.y * 0.1);
  334. }
  335. struct PurpleTrimpot : Trimpot {
  336. Module *module;
  337. bool initialized = false;
  338. PurpleTrimpot();
  339. void step() override;
  340. void reset() override;
  341. void randomize() override;
  342. };
  343. PurpleTrimpot::PurpleTrimpot() : Trimpot() {
  344. setSVG(SVG::load(assetPlugin(plugin, "res/PurpleTrimpot.svg")));
  345. shadow->blurRadius = 0.0;
  346. shadow->opacity = 0.10;
  347. shadow->box.pos = Vec(0.0, box.size.y * 0.05);
  348. }
  349. // FIXME: if we are getting moving inputs and we are hovering
  350. // over the trimpot, we kind of jitter arround.
  351. // maybe run this via an onChange()?
  352. void PurpleTrimpot::step() {
  353. //debug("paramId=%d this->initialized: %d initialized: %d this->value: %f value: %f param.value: %f",
  354. // paramId, this->initialized, initialized, this->value, value, module->params[paramId].value);
  355. if (this->value != module->params[paramId].value) {
  356. if (this != RACK_PLUGIN_UI_HOVERED_WIDGET && this->initialized) {
  357. // this->value = module->params[paramId].value;
  358. setValue(module->params[paramId].value);
  359. } else {
  360. module->params[paramId].value = this->value;
  361. this->initialized |= true;
  362. }
  363. EventChange e;
  364. onChange(e);
  365. }
  366. Trimpot::step();
  367. }
  368. void PurpleTrimpot::reset() {
  369. this->initialized = false;
  370. Trimpot::reset();
  371. }
  372. void PurpleTrimpot::randomize() {
  373. reset();
  374. setValue(rescale(randomUniform(), 0.0f, 1.0f, minValue, maxValue));
  375. }
  376. struct SpecificValueWidget : ModuleWidget
  377. {
  378. SpecificValueWidget(SpecificValue *module);
  379. void step() override;
  380. void onChange(EventChange &e) override;
  381. float prev_volts = 0.0f;
  382. float prev_octave = 4.0f;
  383. float prev_input = 0.0f;
  384. FloatField *volts_field;
  385. HZFloatField *hz_field;
  386. SecondsFloatField *period_field;
  387. NoteNameField *note_name_field;
  388. CentsField *cents_field;
  389. };
  390. SpecificValueWidget::SpecificValueWidget(SpecificValue *module) : ModuleWidget(module)
  391. {
  392. setPanel(SVG::load(assetPlugin(plugin, "res/SpecificValue.svg")));
  393. // TODO: widget with these children?
  394. float y_baseline = 45.0f;
  395. Vec volt_field_size = Vec(70.0f, 22.0f);
  396. Vec hz_field_size = Vec(70.0, 22.0f);
  397. Vec seconds_field_size = Vec(70.0, 22.0f);
  398. float x_pos = 10.0f;
  399. // debug("adding field %d", i);
  400. y_baseline = 45.0f;
  401. volts_field = new FloatField(module);
  402. volts_field->box.pos = Vec(x_pos, y_baseline);
  403. volts_field->box.size = volt_field_size;
  404. volts_field->value = module->params[SpecificValue::VALUE1_PARAM].value;
  405. addChild(volts_field);
  406. y_baseline = 90.0f;
  407. float h_pos = x_pos;
  408. hz_field = new HZFloatField(module);
  409. hz_field->box.pos = Vec(x_pos, y_baseline);
  410. hz_field->box.size = hz_field_size;
  411. hz_field->value = module->hz_value;
  412. addChild(hz_field);
  413. y_baseline = 135.0f;
  414. period_field = new SecondsFloatField(module);
  415. period_field->box.pos = Vec(h_pos, y_baseline);
  416. period_field->box.size = seconds_field_size;
  417. period_field->value = module->period_value;
  418. addChild(period_field);
  419. y_baseline = 180.0f;
  420. note_name_field = new NoteNameField(module);
  421. note_name_field->box.pos = Vec(x_pos, y_baseline);
  422. note_name_field->box.size = Vec(70.0f, 22.0f);
  423. note_name_field->value = module->volt_value;
  424. addChild(note_name_field);
  425. y_baseline += note_name_field->box.size.y;
  426. y_baseline += 5.0f;
  427. // y_baseline += 20.0f;
  428. cents_field = new CentsField(module);
  429. cents_field->box.pos = Vec(x_pos, y_baseline);
  430. cents_field->box.size = Vec(55.0f, 22.0f);
  431. cents_field->value = module->cents_value;
  432. addChild(cents_field);
  433. // y_baseline += period_field->box.size.y;
  434. y_baseline += 20.0f;
  435. float middle = box.size.x / 2.0f;
  436. float in_port_x = 15.0f;
  437. y_baseline += 24.0f + 12.0f;
  438. Port *value_in_port = Port::create<PJ301MPort>(
  439. Vec(in_port_x, y_baseline),
  440. Port::INPUT,
  441. module,
  442. SpecificValue::VALUE1_INPUT);
  443. //value_in_port->box.pos = Vec(middle - value_in_port->box.size.x / 2, y_baseline);
  444. value_in_port->box.pos = Vec(2.0f, y_baseline);
  445. inputs.push_back(value_in_port);
  446. addChild(value_in_port);
  447. // octave trimpot
  448. SmallPurpleTrimpot *octaveTrimpot = ParamWidget::create<SmallPurpleTrimpot>(
  449. Vec(middle, y_baseline + 2.5f),
  450. module,
  451. SpecificValue::OCTAVE_PARAM,
  452. 0.0f, 8.0f, 4.0f);
  453. params.push_back(octaveTrimpot);
  454. octaveTrimpot->box.pos = Vec(middle - octaveTrimpot->box.size.x / 2, y_baseline + 2.5f);
  455. octaveTrimpot->snap = true;
  456. addChild(octaveTrimpot);
  457. float out_port_x = middle + 24.0f;
  458. Port *value_out_port = Port::create<PJ301MPort>(
  459. Vec(out_port_x, y_baseline),
  460. Port::OUTPUT,
  461. module,
  462. SpecificValue::VALUE1_OUTPUT);
  463. outputs.push_back(value_out_port);
  464. value_out_port->box.pos = Vec(box.size.x - value_out_port->box.size.x - 2.0f, y_baseline);
  465. addChild(value_out_port);
  466. y_baseline += value_out_port->box.size.y;
  467. y_baseline += 16.0f;
  468. PurpleTrimpot *trimpot = ParamWidget::create<PurpleTrimpot>(
  469. Vec(middle - 24.0f, y_baseline + 2.5f),
  470. module,
  471. SpecificValue::VALUE1_PARAM,
  472. -10.0f, 10.0f, 0.0f);
  473. //debug(" trimpot: dv: %f v: %f p.value: %f", trimpot->defaultValue, trimpot->value,
  474. // module->params[SpecificValue::VALUE1_PARAM].value);
  475. params.push_back(trimpot);
  476. addChild(trimpot);
  477. }
  478. void SpecificValueWidget::step() {
  479. ModuleWidget::step();
  480. if (prev_volts != module->params[SpecificValue::VALUE1_PARAM].value ||
  481. prev_octave != module->params[SpecificValue::OCTAVE_PARAM].value ||
  482. prev_input != module->params[SpecificValue::VALUE1_INPUT].value) {
  483. // debug("SpVWidget step - emitting EventChange / onChange prev_volts=%f param=%f",
  484. // prev_volts, module->params[SpecificValue::VALUE1_PARAM].value);
  485. prev_volts = module->params[SpecificValue::VALUE1_PARAM].value;
  486. prev_octave = module->params[SpecificValue::OCTAVE_PARAM].value;
  487. prev_input = module->params[SpecificValue::VALUE1_INPUT].value;
  488. EventChange e;
  489. onChange(e);
  490. }
  491. }
  492. void SpecificValueWidget::onChange(EventChange &e) {
  493. // debug("SpvWidget onChange");
  494. ModuleWidget::onChange(e);
  495. volts_field->onChange(e);
  496. hz_field->onChange(e);
  497. period_field->onChange(e);
  498. note_name_field->onChange(e);
  499. cents_field->onChange(e);
  500. }
  501. } // namespace rack_plugin_Alikins
  502. using namespace rack_plugin_Alikins;
  503. RACK_PLUGIN_MODEL_INIT(Alikins, SpecificValue) {
  504. Model *modelSpecificValue = Model::create<SpecificValue, SpecificValueWidget>(
  505. "Alikins", "SpecificValue", "Specific Values", UTILITY_TAG);
  506. return modelSpecificValue;
  507. }