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.

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