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.

488 lines
15KB

  1. #include "Bidoo.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "BidooComponents.hpp"
  4. #include "osdialog.h"
  5. #define DR_WAV_IMPLEMENTATION
  6. #include "dep/dr_wav/dr_wav.h"
  7. #include <vector>
  8. #include "cmath"
  9. #include <iomanip> // setprecision
  10. #include <sstream> // stringstream
  11. using namespace std;
  12. namespace rack_plugin_Bidoo {
  13. struct OUAIVE : Module {
  14. enum ParamIds {
  15. NB_SLICES_PARAM,
  16. TRIG_MODE_PARAM,
  17. READ_MODE_PARAM,
  18. SPEED_PARAM,
  19. NUM_PARAMS
  20. };
  21. enum InputIds {
  22. GATE_INPUT,
  23. POS_INPUT,
  24. NB_SLICES_INPUT,
  25. READ_MODE_INPUT,
  26. SPEED_INPUT,
  27. NUM_INPUTS
  28. };
  29. enum OutputIds {
  30. OUTL_OUTPUT,
  31. OUTR_OUTPUT,
  32. NUM_OUTPUTS
  33. };
  34. enum LightIds {
  35. NUM_LIGHTS
  36. };
  37. bool play = false;
  38. string lastPath;
  39. unsigned int channels;
  40. unsigned int sampleRate;
  41. drwav_uint64 totalSampleCount;
  42. float* pSampleData;
  43. float samplePos = 0.0f;
  44. vector<double> displayBuffL;
  45. vector<double> displayBuffR;
  46. string fileDesc;
  47. bool fileLoaded = false;
  48. int trigMode = 0; // 0 trig 1 gate, 2 sliced
  49. int sliceIndex = -1;
  50. int sliceLength = 0;
  51. int nbSlices = 1;
  52. int readMode = 0; // 0 formward, 1 backward, 2 repeat
  53. float speed;
  54. string displayParams = "";
  55. string displayReadMode = "";
  56. string displaySlices = "";
  57. string displaySpeed;
  58. SchmittTrigger playTrigger;
  59. SchmittTrigger trigModeTrigger;
  60. SchmittTrigger readModeTrigger;
  61. std::mutex mylock;
  62. OUAIVE() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { }
  63. void step() override;
  64. void loadSample(std::string path);
  65. // persistence
  66. json_t *toJson() override {
  67. json_t *rootJ = json_object();
  68. // lastPath
  69. json_object_set_new(rootJ, "lastPath", json_string(lastPath.c_str()));
  70. json_object_set_new(rootJ, "trigMode", json_integer(trigMode));
  71. json_object_set_new(rootJ, "readMode", json_integer(readMode));
  72. return rootJ;
  73. }
  74. void fromJson(json_t *rootJ) override {
  75. // lastPath
  76. json_t *lastPathJ = json_object_get(rootJ, "lastPath");
  77. if (lastPathJ) {
  78. lastPath = json_string_value(lastPathJ);
  79. loadSample(lastPath);
  80. }
  81. json_t *trigModeJ = json_object_get(rootJ, "trigMode");
  82. if (trigModeJ) {
  83. trigMode = json_integer_value(trigModeJ);
  84. }
  85. json_t *readModeJ = json_object_get(rootJ, "readMode");
  86. if (readModeJ) {
  87. readMode = json_integer_value(readModeJ);
  88. }
  89. }
  90. };
  91. void OUAIVE::loadSample(std::string path) {
  92. mylock.lock();
  93. fileLoaded = false;
  94. drwav_free(pSampleData);
  95. pSampleData = drwav_open_and_read_file_f32(path.c_str(), &channels, &sampleRate, &totalSampleCount);
  96. if (pSampleData == NULL) {
  97. fileLoaded = false;
  98. }
  99. else {
  100. vector<double>().swap(displayBuffL);
  101. vector<double>().swap(displayBuffR);
  102. for (unsigned int i=0; i < totalSampleCount; i = i + floor(totalSampleCount/125)) {
  103. displayBuffL.push_back(pSampleData[i]);
  104. if (channels == 2)
  105. displayBuffR.push_back(pSampleData[i+1]);
  106. }
  107. fileDesc = (stringFilename(path)).substr(0,20) + ((stringFilename(path)).length() >=20 ? "...\n" : "\n");
  108. fileDesc += std::to_string(sampleRate) + " Hz\n";
  109. fileLoaded = true;
  110. }
  111. mylock.unlock();
  112. }
  113. void OUAIVE::step() {
  114. if (trigModeTrigger.process(params[TRIG_MODE_PARAM].value)) {
  115. trigMode = (((int)trigMode + 1) % 3);
  116. }
  117. if (inputs[READ_MODE_INPUT].active) {
  118. readMode = round(rescale(inputs[READ_MODE_INPUT].value, 0.0f,10.0f,0.0f,2.0f));
  119. } else if (readModeTrigger.process(params[READ_MODE_PARAM].value + inputs[READ_MODE_INPUT].value)) {
  120. readMode = (((int)readMode + 1) % 3);
  121. }
  122. nbSlices = clamp(roundl(params[NB_SLICES_PARAM].value + inputs[NB_SLICES_INPUT].value), 1, 128);
  123. speed = clamp(params[SPEED_PARAM].value + inputs[SPEED_INPUT].value, 0.2f, 10.0f);
  124. stringstream stream;
  125. stream << fixed << setprecision(1) << speed;
  126. string s = stream.str();
  127. displaySpeed = "x" + s;
  128. if (trigMode == 0) {
  129. displayParams = "TRIG";
  130. if (readMode == 0) {
  131. displayReadMode = "►";
  132. } else if (readMode == 2) {
  133. displayReadMode = "►►";
  134. }
  135. else {
  136. displayReadMode = "◄";
  137. }
  138. } else if (trigMode == 1) {
  139. displayParams = "GATE";
  140. } else if (trigMode == 2) {
  141. displayParams = "SLICE ";
  142. displaySlices = "|" + std::to_string(nbSlices) + "|";
  143. if (readMode == 0) {
  144. displayReadMode = "►";
  145. } else if (readMode == 2) {
  146. displayReadMode = "►►";
  147. }
  148. else {
  149. displayReadMode = "◄";
  150. }
  151. }
  152. if (fileLoaded) {
  153. sliceLength = clamp(totalSampleCount / nbSlices, 1, totalSampleCount);
  154. if ((trigMode == 0) && (playTrigger.process(inputs[GATE_INPUT].value))) {
  155. play = true;
  156. samplePos = clamp((int)(inputs[POS_INPUT].value * totalSampleCount / 10), 0 , totalSampleCount - 1);
  157. } else if (trigMode == 1) {
  158. play = (inputs[GATE_INPUT].value > 0);
  159. samplePos = clamp((int)(inputs[POS_INPUT].value * totalSampleCount / 10), 0 , totalSampleCount - 1);
  160. } else if ((trigMode == 2) && (playTrigger.process(inputs[GATE_INPUT].value))) {
  161. play = true;
  162. if (inputs[POS_INPUT].active)
  163. sliceIndex = clamp((int)(inputs[POS_INPUT].value * nbSlices / 10), 0, nbSlices);
  164. else
  165. sliceIndex = (sliceIndex+1)%nbSlices;
  166. if (readMode != 1)
  167. samplePos = clamp(sliceIndex*sliceLength, 0, totalSampleCount);
  168. else
  169. samplePos = clamp((sliceIndex + 1) * sliceLength - 1, 0 , totalSampleCount);
  170. }
  171. if ((play) && (samplePos>=0) && (samplePos < totalSampleCount)) {
  172. mylock.lock();
  173. //calulate outputs
  174. if (channels == 1) {
  175. outputs[OUTL_OUTPUT].value = 10.0f * pSampleData[(unsigned int)floor(samplePos)];
  176. outputs[OUTR_OUTPUT].value = 10.0f * pSampleData[(unsigned int)floor(samplePos)];
  177. }
  178. else if (channels == 2) {
  179. if (outputs[OUTL_OUTPUT].active && outputs[OUTR_OUTPUT].active) {
  180. outputs[OUTL_OUTPUT].value = 10.0f * pSampleData[(unsigned int)floor(samplePos)];
  181. outputs[OUTR_OUTPUT].value = 10.0f * pSampleData[(unsigned int)floor(samplePos)+1];
  182. }
  183. else {
  184. outputs[OUTL_OUTPUT].value = 10.0f * (pSampleData[(unsigned int)floor(samplePos)] + pSampleData[(unsigned int)floor(samplePos)+1]) / 2;
  185. outputs[OUTR_OUTPUT].value = 10.0f * (pSampleData[(unsigned int)floor(samplePos)] + pSampleData[(unsigned int)floor(samplePos)+1]) / 2;
  186. }
  187. }
  188. mylock.unlock();
  189. //shift samplePos
  190. if (trigMode == 0) {
  191. if (readMode != 1)
  192. samplePos = samplePos + speed * channels;
  193. else
  194. samplePos = samplePos - speed * channels;
  195. //manage eof readMode
  196. if ((readMode == 0) && (samplePos >= totalSampleCount))
  197. play = false;
  198. else if ((readMode == 1) && (samplePos <=0))
  199. play = false;
  200. else if ((readMode == 2) && (samplePos >= totalSampleCount))
  201. samplePos = clamp((int)(inputs[POS_INPUT].value * totalSampleCount / 10), 0 , totalSampleCount -1);
  202. }
  203. else if (trigMode == 2)
  204. {
  205. if (readMode != 1)
  206. samplePos = samplePos + speed * channels;
  207. else
  208. samplePos = samplePos - speed * channels;
  209. //update diplay slices
  210. displaySlices = "|" + std::to_string(nbSlices) + "|";
  211. //manage eof readMode
  212. if ((readMode == 0) && ((samplePos >= (sliceIndex+1) * sliceLength) || (samplePos >= totalSampleCount)))
  213. play = false;
  214. if ((readMode == 1) && ((samplePos <= (sliceIndex) * sliceLength) || (samplePos <= 0)))
  215. play = false;
  216. if ((readMode == 2) && ((samplePos >= (sliceIndex+1) * sliceLength) || (samplePos >= totalSampleCount)))
  217. samplePos = clamp(sliceIndex*sliceLength, 0 , totalSampleCount);
  218. }
  219. }
  220. else if (samplePos == totalSampleCount)
  221. play = false;
  222. }
  223. }
  224. struct OUAIVEDisplay : TransparentWidget {
  225. OUAIVE *module;
  226. int frame = 0;
  227. shared_ptr<Font> font;
  228. string displayParams;
  229. OUAIVEDisplay() {
  230. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  231. }
  232. void draw(NVGcontext *vg) override {
  233. nvgFontSize(vg, 12);
  234. nvgFontFaceId(vg, font->handle);
  235. nvgStrokeWidth(vg, 1);
  236. nvgTextLetterSpacing(vg, -2);
  237. nvgFillColor(vg, YELLOW_BIDOO);
  238. nvgTextBox(vg, 5, 3,120, module->fileDesc.c_str(), NULL);
  239. nvgFontSize(vg, 14);
  240. nvgFillColor(vg, YELLOW_BIDOO);
  241. nvgTextBox(vg, 5, 55,40, module->displayParams.c_str(), NULL);
  242. if (module->trigMode != 1)
  243. nvgTextBox(vg, 95, 55,30, module->displaySpeed.c_str(), NULL);
  244. if (module->trigMode == 0) {
  245. nvgTextBox(vg, 45, 55,20, module->displayReadMode.c_str(), NULL);
  246. }
  247. if (module->trigMode == 2) {
  248. nvgTextBox(vg, 45, 55,20, module->displayReadMode.c_str(), NULL);
  249. nvgTextBox(vg, 62, 55,40, module->displaySlices.c_str(), NULL);
  250. }
  251. if (module->fileLoaded) {
  252. // Draw play line
  253. nvgStrokeColor(vg, LIGHTBLUE_BIDOO);
  254. {
  255. nvgBeginPath(vg);
  256. nvgStrokeWidth(vg, 2);
  257. nvgMoveTo(vg, (int)(module->samplePos * 125 / module->totalSampleCount) , 70);
  258. nvgLineTo(vg, (int)(module->samplePos * 125 / module->totalSampleCount) , 150);
  259. nvgClosePath(vg);
  260. }
  261. nvgStroke(vg);
  262. if (module->channels == 1) {
  263. // Draw ref line
  264. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x30));
  265. nvgStrokeWidth(vg, 1);
  266. {
  267. nvgBeginPath(vg);
  268. nvgMoveTo(vg, 0, 110);
  269. nvgLineTo(vg, 130, 110);
  270. nvgClosePath(vg);
  271. }
  272. nvgStroke(vg);
  273. // Draw waveform
  274. nvgStrokeColor(vg, PINK_BIDOO);
  275. nvgSave(vg);
  276. Rect b = Rect(Vec(0, 70), Vec(125, 80));
  277. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  278. nvgBeginPath(vg);
  279. for (unsigned int i = 0; i < module->displayBuffL.size(); i++) {
  280. float x, y;
  281. x = (float)i / (module->displayBuffL.size() - 1.0f);
  282. y = module->displayBuffL[i] / 2.0f + 0.5f;
  283. Vec p;
  284. p.x = b.pos.x + b.size.x * x;
  285. p.y = b.pos.y + b.size.y * (1.0f - y);
  286. if (i == 0)
  287. nvgMoveTo(vg, p.x, p.y);
  288. else
  289. nvgLineTo(vg, p.x, p.y);
  290. }
  291. nvgLineCap(vg, NVG_ROUND);
  292. nvgMiterLimit(vg, 2.0);
  293. nvgStrokeWidth(vg, 1);
  294. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  295. nvgStroke(vg);
  296. nvgResetScissor(vg);
  297. nvgRestore(vg);
  298. }
  299. else {
  300. // Draw ref line
  301. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x30));
  302. {
  303. nvgBeginPath(vg);
  304. nvgMoveTo(vg, 0, 90);
  305. nvgLineTo(vg, 130, 90);
  306. nvgMoveTo(vg, 0, 130);
  307. nvgLineTo(vg, 130, 130);
  308. nvgClosePath(vg);
  309. }
  310. nvgStroke(vg);
  311. // Draw waveform
  312. nvgStrokeColor(vg, PINK_BIDOO);
  313. nvgSave(vg);
  314. Rect b = Rect(Vec(0, 70), Vec(125, 40));
  315. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  316. nvgBeginPath(vg);
  317. for (unsigned int i = 0; i < module->displayBuffL.size(); i++) {
  318. float x, y;
  319. x = (float)i / (module->displayBuffL.size() - 1.0f);
  320. y = module->displayBuffL[i] / 2.0f + 0.5f;
  321. Vec p;
  322. p.x = b.pos.x + b.size.x * x;
  323. p.y = b.pos.y + b.size.y * (1.0f - y);
  324. if (i == 0)
  325. nvgMoveTo(vg, p.x, p.y);
  326. else
  327. nvgLineTo(vg, p.x, p.y);
  328. }
  329. nvgLineCap(vg, NVG_ROUND);
  330. nvgMiterLimit(vg, 2.0);
  331. nvgStrokeWidth(vg, 1);
  332. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  333. nvgStroke(vg);
  334. b = Rect(Vec(0, 110), Vec(125, 40));
  335. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  336. nvgBeginPath(vg);
  337. for (unsigned int i = 0; i < module->displayBuffR.size(); i++) {
  338. float x, y;
  339. x = (float)i / (module->displayBuffR.size() - 1.0f);
  340. y = module->displayBuffR[i] / 2.0f + 0.5f;
  341. Vec p;
  342. p.x = b.pos.x + b.size.x * x;
  343. p.y = b.pos.y + b.size.y * (1.0f - y);
  344. if (i == 0)
  345. nvgMoveTo(vg, p.x, p.y);
  346. else
  347. nvgLineTo(vg, p.x, p.y);
  348. }
  349. nvgLineCap(vg, NVG_ROUND);
  350. nvgMiterLimit(vg, 2.0f);
  351. nvgStrokeWidth(vg, 1);
  352. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  353. nvgStroke(vg);
  354. nvgResetScissor(vg);
  355. nvgRestore(vg);
  356. }
  357. //draw slices
  358. if (module->trigMode == 2) {
  359. for (int i = 1; i < module->nbSlices; i++) {
  360. nvgStrokeColor(vg, YELLOW_BIDOO);
  361. {
  362. nvgBeginPath(vg);
  363. nvgStrokeWidth(vg, 1);
  364. nvgMoveTo(vg, (int)(i * module->sliceLength * 125 / module->totalSampleCount) , 70);
  365. nvgLineTo(vg, (int)(i * module->sliceLength * 125 / module->totalSampleCount) , 150);
  366. nvgClosePath(vg);
  367. }
  368. nvgStroke(vg);
  369. }
  370. }
  371. }
  372. }
  373. };
  374. struct OUAIVEWidget : ModuleWidget {
  375. Menu *createContextMenu() override;
  376. OUAIVEWidget(OUAIVE *module) : ModuleWidget(module) {
  377. setPanel(SVG::load(assetPlugin(plugin, "res/OUAIVE.svg")));
  378. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  379. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  380. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  381. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  382. {
  383. OUAIVEDisplay *display = new OUAIVEDisplay();
  384. display->module = module;
  385. display->box.pos = Vec(5, 40);
  386. display->box.size = Vec(130, 250);
  387. addChild(display);
  388. }
  389. static const float portX0[4] = {34, 67, 101};
  390. addParam(ParamWidget::create<BlueCKD6>(Vec(portX0[0]-25, 215), module, OUAIVE::TRIG_MODE_PARAM, 0.0, 2.0, 0.0));
  391. addParam(ParamWidget::create<BlueCKD6>(Vec(portX0[1]-14, 215), module, OUAIVE::READ_MODE_PARAM, 0.0, 2.0, 0.0));
  392. addInput(Port::create<TinyPJ301MPort>(Vec(portX0[2]+5, 222), Port::INPUT, module, OUAIVE::READ_MODE_INPUT));
  393. addParam(ParamWidget::create<BidooBlueTrimpot>(Vec(portX0[1]-9, 250), module, OUAIVE::NB_SLICES_PARAM, 1.0, 128.01, 1.0));
  394. addInput(Port::create<TinyPJ301MPort>(Vec(portX0[2]+5, 252), Port::INPUT, module, OUAIVE::NB_SLICES_INPUT));
  395. addParam(ParamWidget::create<BidooBlueTrimpot>(Vec(portX0[1]-9, 275), module, OUAIVE::SPEED_PARAM, -0.05, 10, 1.0));
  396. addInput(Port::create<TinyPJ301MPort>(Vec(portX0[2]+5, 277), Port::INPUT, module, OUAIVE::SPEED_INPUT));
  397. addInput(Port::create<PJ301MPort>(Vec(portX0[0]-25, 321), Port::INPUT, module, OUAIVE::GATE_INPUT));
  398. addInput(Port::create<PJ301MPort>(Vec(portX0[1]-19, 321), Port::INPUT, module, OUAIVE::POS_INPUT));
  399. addOutput(Port::create<TinyPJ301MPort>(Vec(portX0[2]-13, 331), Port::OUTPUT, module, OUAIVE::OUTL_OUTPUT));
  400. addOutput(Port::create<TinyPJ301MPort>(Vec(portX0[2]+11, 331), Port::OUTPUT, module, OUAIVE::OUTR_OUTPUT));
  401. }
  402. };
  403. struct OUAIVEItem : MenuItem {
  404. OUAIVE *ouaive;
  405. void onAction(EventAction &e) override {
  406. std::string dir = ouaive->lastPath.empty() ? assetLocal("") : stringDirectory(ouaive->lastPath);
  407. char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL);
  408. if (path) {
  409. ouaive->play = false;
  410. ouaive->fileLoaded = false;
  411. ouaive->loadSample(path);
  412. ouaive->samplePos = 0;
  413. ouaive->lastPath = path;
  414. ouaive->sliceIndex = -1;
  415. free(path);
  416. }
  417. }
  418. };
  419. Menu *OUAIVEWidget::createContextMenu() {
  420. Menu *menu = ModuleWidget::createContextMenu();
  421. MenuLabel *spacerLabel = new MenuLabel();
  422. menu->addChild(spacerLabel);
  423. OUAIVE *ouaive = dynamic_cast<OUAIVE*>(module);
  424. assert(ouaive);
  425. OUAIVEItem *sampleItem = new OUAIVEItem();
  426. sampleItem->text = "Load sample";
  427. sampleItem->ouaive = ouaive;
  428. menu->addChild(sampleItem);
  429. return menu;
  430. }
  431. } // namespace rack_plugin_Bidoo
  432. using namespace rack_plugin_Bidoo;
  433. RACK_PLUGIN_MODEL_INIT(Bidoo, OUAIVE) {
  434. Model *modelOUAIVE = Model::create<OUAIVE, OUAIVEWidget>("Bidoo","OUAIve", "OUAIve player", SAMPLER_TAG, GRANULAR_TAG);
  435. return modelOUAIVE;
  436. }