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.

889 lines
27KB

  1. #include "Bidoo.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "BidooComponents.hpp"
  4. #include "osdialog.h"
  5. #include "dep/dr_wav/dr_wav.h"
  6. #include <vector>
  7. #include "cmath"
  8. #include <iomanip> // setprecision
  9. #include <sstream> // stringstream
  10. #include <algorithm>
  11. #include "window.hpp"
  12. using namespace std;
  13. namespace rack_plugin_Bidoo {
  14. struct CANARD : Module {
  15. enum ParamIds {
  16. RECORD_PARAM,
  17. SAMPLE_START_PARAM,
  18. LOOP_LENGTH_PARAM,
  19. READ_MODE_PARAM,
  20. SPEED_PARAM,
  21. FADE_PARAM,
  22. MODE_PARAM,
  23. SLICE_PARAM,
  24. CLEAR_PARAM,
  25. THRESHOLD_PARAM,
  26. NUM_PARAMS
  27. };
  28. enum InputIds {
  29. INL_INPUT,
  30. INR_INPUT,
  31. TRIG_INPUT,
  32. GATE_INPUT,
  33. SAMPLE_START_INPUT,
  34. LOOP_LENGTH_INPUT,
  35. READ_MODE_INPUT,
  36. SPEED_INPUT,
  37. RECORD_INPUT,
  38. FADE_INPUT,
  39. SLICE_INPUT,
  40. CLEAR_INPUT,
  41. NUM_INPUTS
  42. };
  43. enum OutputIds {
  44. OUTL_OUTPUT,
  45. OUTR_OUTPUT,
  46. EOC_OUTPUT,
  47. NUM_OUTPUTS
  48. };
  49. enum LightIds {
  50. REC_LIGHT,
  51. NUM_LIGHTS
  52. };
  53. bool play = false;
  54. bool record = false;
  55. unsigned int channels = 2;
  56. unsigned int sampleRate = 0;
  57. drwav_uint64 totalSampleCount = 0;
  58. vector<vector<float>> playBuffer, recordBuffer;
  59. float samplePos = 0.0f, sampleStart = 0.0f, loopLength = 0.0f, fadeLenght = 0.0f, fadeCoeff = 1.0f, speedFactor = 1.0f;
  60. size_t prevPlayedSlice = 0;
  61. size_t playedSlice = 0;
  62. bool changedSlice = false;
  63. int readMode = 0; // 0 formward, 1 backward, 2 repeat
  64. float speed;
  65. std::vector<int> slices;
  66. int selected = -1;
  67. bool deleteFlag = false;
  68. int addSliceMarker = -1;
  69. bool addSliceMarkerFlag = false;
  70. int deleteSliceMarker = -1;
  71. bool deleteSliceMarkerFlag = false;
  72. size_t index = 0;
  73. float prevGateState = 0.0f;
  74. float prevTrigState = 0.0f;
  75. string lastPath;
  76. string waveFileName;
  77. string waveExtension;
  78. bool loading = false;
  79. SchmittTrigger trigTrigger;
  80. SchmittTrigger recordTrigger;
  81. SchmittTrigger clearTrigger;
  82. PulseGenerator eocPulse;
  83. std::mutex mylock;
  84. bool newStop = false;
  85. CANARD() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  86. playBuffer.resize(2);
  87. playBuffer[0].resize(0);
  88. playBuffer[1].resize(0);
  89. recordBuffer.resize(2);
  90. recordBuffer[0].resize(0);
  91. recordBuffer[1].resize(0);
  92. }
  93. void step() override;
  94. void calcLoop();
  95. void initPos();
  96. void loadSample(std::string path);
  97. // persistence
  98. json_t *toJson() override {
  99. json_t *rootJ = json_object();
  100. // lastPath
  101. json_object_set_new(rootJ, "lastPath", json_string(lastPath.c_str()));
  102. json_t *slicesJ = json_array();
  103. for (size_t i = 0; i<slices.size() ; i++) {
  104. json_t *sliceJ = json_integer(slices[i]);
  105. json_array_append_new(slicesJ, sliceJ);
  106. }
  107. json_object_set_new(rootJ, "slices", slicesJ);
  108. return rootJ;
  109. }
  110. void fromJson(json_t *rootJ) override {
  111. json_t *lastPathJ = json_object_get(rootJ, "lastPath");
  112. if (lastPathJ) {
  113. lastPath = json_string_value(lastPathJ);
  114. waveFileName = stringFilename(lastPath);
  115. waveExtension = stringExtension(lastPath);
  116. loadSample(lastPath);
  117. if (totalSampleCount>0) {
  118. json_t *slicesJ = json_object_get(rootJ, "slices");
  119. if (slicesJ) {
  120. size_t i;
  121. json_t *sliceJ;
  122. json_array_foreach(slicesJ, i, sliceJ) {
  123. if (i != 0)
  124. slices.push_back(json_integer_value(sliceJ));
  125. }
  126. }
  127. }
  128. }
  129. }
  130. };
  131. void CANARD::loadSample(std::string path) {
  132. loading = true;
  133. unsigned int c;
  134. unsigned int sr;
  135. drwav_uint64 sc;
  136. float* pSampleData;
  137. pSampleData = drwav_open_and_read_file_f32(path.c_str(), &c, &sr, &sc);
  138. if (pSampleData != NULL) {
  139. lastPath = path;
  140. waveFileName = stringFilename(path);
  141. waveExtension = stringExtension(path);
  142. channels = c;
  143. sampleRate = sr;
  144. slices.clear();
  145. slices.push_back(0);
  146. playBuffer[0].clear();
  147. playBuffer[1].clear();
  148. for (unsigned int i=0; i < sc; i = i + c) {
  149. playBuffer[0].push_back(pSampleData[i]);
  150. if (channels == 2)
  151. playBuffer[1].push_back((float)pSampleData[i+1]);
  152. else
  153. playBuffer[1].push_back((float)pSampleData[i]);
  154. }
  155. totalSampleCount = playBuffer[0].size();
  156. drwav_free(pSampleData);
  157. }
  158. loading = false;
  159. }
  160. void CANARD::calcLoop() {
  161. prevPlayedSlice = index;
  162. index = 0;
  163. int sliceStart = 0;;
  164. int sliceEnd = totalSampleCount > 0 ? totalSampleCount - 1 : 0;
  165. if ((params[MODE_PARAM].value == 1) && (slices.size()>0))
  166. {
  167. index = round(clamp(params[SLICE_PARAM].value + inputs[SLICE_INPUT].value, 0.0f,10.0f)*(slices.size()-1)/10);
  168. sliceStart = slices[index];
  169. sliceEnd = (index < (slices.size() - 1)) ? (slices[index+1] - 1) : (totalSampleCount - 1);
  170. }
  171. if (totalSampleCount > 0) {
  172. sampleStart = rescale(clamp(inputs[SAMPLE_START_INPUT].value + params[SAMPLE_START_PARAM].value, 0.0f, 10.0f), 0.0f, 10.0f, sliceStart, sliceEnd);
  173. loopLength = clamp(rescale(clamp(inputs[LOOP_LENGTH_INPUT].value + params[LOOP_LENGTH_PARAM].value, 0.0f, 10.0f), 0.0f, 10.0f, 0.0f, sliceEnd - sliceStart + 1),1.0f,sliceEnd-sampleStart+1);
  174. fadeLenght = rescale(clamp(inputs[FADE_INPUT].value + params[FADE_PARAM].value, 0.0f, 10.0f), 0.0f, 10.0f,0.0f, floor(loopLength/2));
  175. }
  176. else {
  177. loopLength = 0;
  178. sampleStart = 0;
  179. fadeLenght = 0;
  180. }
  181. playedSlice = index;
  182. }
  183. void CANARD::initPos() {
  184. if ((inputs[SPEED_INPUT].value + params[SPEED_PARAM].value)>=0)
  185. {
  186. samplePos = sampleStart;
  187. }
  188. else
  189. {
  190. samplePos = sampleStart + loopLength;
  191. }
  192. speedFactor = 1.0f;
  193. }
  194. void CANARD::step() {
  195. if (!loading) {
  196. if (clearTrigger.process(inputs[CLEAR_INPUT].value + params[CLEAR_PARAM].value))
  197. {
  198. mylock.lock();
  199. playBuffer[0].clear();
  200. playBuffer[1].clear();
  201. totalSampleCount = 0;
  202. slices.clear();
  203. mylock.unlock();
  204. lastPath = "";
  205. waveFileName = "";
  206. waveExtension = "";
  207. }
  208. if ((selected>=0) && (deleteFlag)) {
  209. int nbSample=0;
  210. if ((size_t)selected<(slices.size()-1)) {
  211. nbSample = slices[selected + 1] - slices[selected] - 1;
  212. mylock.lock();
  213. playBuffer[0].erase(playBuffer[0].begin() + slices[selected], playBuffer[0].begin() + slices[selected + 1]-1);
  214. playBuffer[1].erase(playBuffer[1].begin() + slices[selected], playBuffer[1].begin() + slices[selected + 1]-1);
  215. mylock.unlock();
  216. }
  217. else {
  218. nbSample = totalSampleCount - slices[selected];
  219. mylock.lock();
  220. playBuffer[0].erase(playBuffer[0].begin() + slices[selected], playBuffer[0].end());
  221. playBuffer[1].erase(playBuffer[1].begin() + slices[selected], playBuffer[1].end());
  222. mylock.unlock();
  223. }
  224. slices.erase(slices.begin()+selected);
  225. totalSampleCount = playBuffer[0].size();
  226. for (size_t i = selected; i < slices.size(); i++)
  227. {
  228. slices[i] = slices[i]-nbSample;
  229. }
  230. selected = -1;
  231. deleteFlag = false;
  232. calcLoop();
  233. }
  234. if ((addSliceMarker>=0) && (addSliceMarkerFlag)) {
  235. if (std::find(slices.begin(), slices.end(), addSliceMarker) != slices.end()) {
  236. addSliceMarker = -1;
  237. addSliceMarkerFlag = false;
  238. }
  239. else {
  240. auto it = std::upper_bound(slices.begin(), slices.end(), addSliceMarker);
  241. mylock.lock();
  242. slices.insert(it, addSliceMarker);
  243. mylock.unlock();
  244. addSliceMarker = -1;
  245. addSliceMarkerFlag = false;
  246. calcLoop();
  247. }
  248. }
  249. if ((deleteSliceMarker>=0) && (deleteSliceMarkerFlag)) {
  250. if (std::find(slices.begin(), slices.end(), deleteSliceMarker) != slices.end()) {
  251. mylock.lock();
  252. slices.erase(std::find(slices.begin(), slices.end(), deleteSliceMarker));
  253. mylock.unlock();
  254. deleteSliceMarker = -1;
  255. deleteSliceMarkerFlag = false;
  256. calcLoop();
  257. }
  258. }
  259. if (recordTrigger.process(inputs[RECORD_INPUT].value + params[RECORD_PARAM].value))
  260. {
  261. if(record) {
  262. if (floor(params[MODE_PARAM].value) == 0) {
  263. mylock.lock();
  264. slices.clear();
  265. slices.push_back(0);
  266. playBuffer.resize(2);
  267. playBuffer[0].resize((int)recordBuffer[0].size());
  268. playBuffer[1].resize((int)recordBuffer[0].size());
  269. for (int i = 0; i < (int)recordBuffer[0].size(); i++) {
  270. playBuffer[0][i] = recordBuffer[0][i];
  271. playBuffer[1][i] = recordBuffer[1][i];
  272. }
  273. totalSampleCount = playBuffer[0].size();
  274. mylock.unlock();
  275. lastPath = "";
  276. waveFileName = "";
  277. waveExtension = "";
  278. }
  279. else {
  280. mylock.lock();
  281. slices.push_back(totalSampleCount > 0 ? (totalSampleCount-1) : 0);
  282. playBuffer[0].insert(playBuffer[0].end(), recordBuffer[0].begin(), recordBuffer[0].end());
  283. playBuffer[1].insert(playBuffer[1].end(), recordBuffer[1].begin(), recordBuffer[1].end());
  284. totalSampleCount = playBuffer[0].size();
  285. mylock.unlock();
  286. }
  287. mylock.lock();
  288. recordBuffer[0].resize(0);
  289. recordBuffer[1].resize(0);
  290. mylock.unlock();
  291. lights[REC_LIGHT].value = 0.0f;
  292. }
  293. record = !record;
  294. }
  295. if (record) {
  296. lights[REC_LIGHT].value = 10.0f;
  297. mylock.lock();
  298. recordBuffer[0].push_back(inputs[INL_INPUT].value/10);
  299. recordBuffer[1].push_back(inputs[INR_INPUT].value/10);
  300. mylock.unlock();
  301. }
  302. int trigMode = inputs[TRIG_INPUT].active ? 1 : (inputs[GATE_INPUT].active ? 2 : 0);
  303. int readMode = round(clamp(inputs[READ_MODE_INPUT].value + params[READ_MODE_PARAM].value,0.0f,2.0f));
  304. speed = inputs[SPEED_INPUT].value + params[SPEED_PARAM].value;
  305. calcLoop();
  306. if (trigMode == 1) {
  307. if (trigTrigger.process(inputs[TRIG_INPUT].value) && (prevTrigState == 0.0f))
  308. {
  309. initPos();
  310. play = true;
  311. }
  312. else {
  313. if ((readMode == 0) && (speed>=0) && (samplePos == (sampleStart+loopLength))) {
  314. play = false;
  315. if (newStop) {
  316. eocPulse.trigger(10 / engineGetSampleRate());
  317. newStop = false;
  318. }
  319. }
  320. else if ((readMode == 0) && (speed<0) && (samplePos == sampleStart)) {
  321. play = false;
  322. if (newStop) {
  323. eocPulse.trigger(10 / engineGetSampleRate());
  324. newStop = false;
  325. }
  326. }
  327. else if ((readMode == 1) && (speed>=0) && (samplePos == (sampleStart+loopLength))) {
  328. initPos();
  329. }
  330. else if ((readMode == 1) && (speed<0) && (samplePos == sampleStart)) {
  331. initPos();
  332. }
  333. else if ((readMode == 2) && ((samplePos == (sampleStart)) || (samplePos == (sampleStart+loopLength)))) {
  334. speedFactor = -1 * speedFactor;
  335. samplePos = samplePos + speedFactor * speed;
  336. }
  337. else {
  338. samplePos = samplePos + speedFactor * speed;
  339. }
  340. }
  341. samplePos = clamp(samplePos,sampleStart,sampleStart+loopLength);
  342. }
  343. else if (trigMode == 2)
  344. {
  345. if (inputs[GATE_INPUT].value>0)
  346. {
  347. if (prevGateState == 0.0f) {
  348. initPos();
  349. play = true;
  350. }
  351. else {
  352. if ((readMode == 0) && (speed>=0) && (samplePos == (sampleStart+loopLength))) {
  353. play = false;
  354. if (newStop) {
  355. eocPulse.trigger(10 / engineGetSampleRate());
  356. newStop = false;
  357. }
  358. }
  359. else if ((readMode == 0) && (speed<0) && (samplePos == sampleStart)) {
  360. play = false;
  361. if (newStop) {
  362. eocPulse.trigger(10 / engineGetSampleRate());
  363. newStop = false;
  364. }
  365. }
  366. else if ((readMode == 1) && (speed>=0) && (samplePos == (sampleStart+loopLength))) {
  367. initPos();
  368. }
  369. else if ((readMode == 1) && (speed<0) && (samplePos == sampleStart)) {
  370. initPos();
  371. }
  372. else if ((readMode == 2) && ((samplePos == (sampleStart)) || (samplePos == (sampleStart+loopLength)))) {
  373. speedFactor = -1 * speedFactor;
  374. samplePos = samplePos + speedFactor * speed;
  375. }
  376. else {
  377. samplePos = samplePos + speedFactor * speed;
  378. }
  379. }
  380. samplePos = clamp(samplePos,sampleStart,sampleStart+loopLength);
  381. }
  382. else {
  383. play = false;
  384. }
  385. }
  386. prevGateState = inputs[GATE_INPUT].value;
  387. prevTrigState = inputs[TRIG_INPUT].value;
  388. if (play) {
  389. newStop = true;
  390. if (samplePos<totalSampleCount) {
  391. if (fadeLenght>1000) {
  392. if ((samplePos-sampleStart)<fadeLenght)
  393. fadeCoeff = rescale(samplePos-sampleStart,0.0f,fadeLenght,0.0f,1.0f);
  394. else if ((sampleStart+loopLength-samplePos)<fadeLenght)
  395. fadeCoeff = rescale(sampleStart+loopLength-samplePos,fadeLenght,0.0f,1.0f,0.0f);
  396. else
  397. fadeCoeff = 1.0f;
  398. }
  399. else
  400. fadeCoeff = 1.0f;
  401. outputs[OUTL_OUTPUT].value = playBuffer[0][floor(samplePos)]*fadeCoeff*5;
  402. outputs[OUTR_OUTPUT].value = playBuffer[1][floor(samplePos)]*fadeCoeff*5;
  403. }
  404. }
  405. else {
  406. outputs[OUTL_OUTPUT].value = 0.0f;
  407. outputs[OUTR_OUTPUT].value = 0.0f;
  408. }
  409. }
  410. else {
  411. outputs[OUTL_OUTPUT].value = 0.0f;
  412. outputs[OUTR_OUTPUT].value = 0.0f;
  413. }
  414. outputs[EOC_OUTPUT].value = eocPulse.process(1 / engineGetSampleRate()) ? 10.0f : 0.0f;
  415. }
  416. struct CANARDWidget : ModuleWidget {
  417. CANARDWidget(CANARD *module);
  418. Menu *createContextMenu() override;
  419. };
  420. struct CANARDDisplay : OpaqueWidget {
  421. CANARD *module;
  422. shared_ptr<Font> font;
  423. const float width = 175.0f;
  424. const float height = 50.0f;
  425. float zoomWidth = 175.0f;
  426. float zoomLeftAnchor = 0.0f;
  427. int refIdx = 0;
  428. float refX = 0.0f;
  429. CANARDDisplay() {
  430. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  431. }
  432. void onMouseDown(EventMouseDown &e) override {
  433. if (module->slices.size()>0) {
  434. refX = e.pos.x;
  435. refIdx = ((e.pos.x - zoomLeftAnchor)/zoomWidth)*(float)module->totalSampleCount;
  436. module->addSliceMarker = refIdx;
  437. auto lower = std::lower_bound(module->slices.begin(), module->slices.end(), refIdx);
  438. module->selected = distance(module->slices.begin(),lower-1);
  439. module->deleteSliceMarker = *(lower-1);
  440. }
  441. if (e.button == 0)
  442. OpaqueWidget::onMouseDown(e);
  443. else {
  444. CANARDWidget *cANARdWidget = dynamic_cast<CANARDWidget*>(parent);
  445. cANARdWidget->createContextMenu();
  446. }
  447. }
  448. void onDragStart(EventDragStart &e) override {
  449. windowCursorLock();
  450. OpaqueWidget::onDragStart(e);
  451. }
  452. void onDragMove(EventDragMove &e) override {
  453. float zoom = 1.0f;
  454. if (e.mouseRel.y > 0.0f) {
  455. zoom = 1.0f/(windowIsShiftPressed() ? 2.0f : 1.1f);
  456. }
  457. else if (e.mouseRel.y < 0.0f) {
  458. zoom = windowIsShiftPressed() ? 2.0f : 1.1f;
  459. }
  460. zoomWidth = clamp(zoomWidth*zoom,width,zoomWidth*(windowIsShiftPressed() ? 2.0f : 1.1f));
  461. zoomLeftAnchor = clamp(refX - (refX - zoomLeftAnchor)*zoom + e.mouseRel.x, width - zoomWidth,0.0f);
  462. OpaqueWidget::onDragMove(e);
  463. }
  464. void onDragEnd(EventDragEnd &e) override {
  465. windowCursorUnlock();
  466. OpaqueWidget::onDragEnd(e);
  467. }
  468. void draw(NVGcontext *vg) override {
  469. module->mylock.lock();
  470. std::vector<float> vL(module->playBuffer[0]);
  471. std::vector<float> vR(module->playBuffer[1]);
  472. std::vector<int> s(module->slices);
  473. module->mylock.unlock();
  474. size_t nbSample = vL.size();
  475. // Draw play line
  476. if (!module->loading) {
  477. nvgStrokeColor(vg, LIGHTBLUE_BIDOO);
  478. {
  479. nvgBeginPath(vg);
  480. nvgStrokeWidth(vg, 2);
  481. if (nbSample>0) {
  482. nvgMoveTo(vg, module->samplePos * zoomWidth / nbSample + zoomLeftAnchor, 0);
  483. nvgLineTo(vg, module->samplePos * zoomWidth / nbSample + zoomLeftAnchor, 2*height+10);
  484. }
  485. else {
  486. nvgMoveTo(vg, 0, 0);
  487. nvgLineTo(vg, 0, 2*height+10);
  488. }
  489. nvgClosePath(vg);
  490. }
  491. nvgStroke(vg);
  492. }
  493. // Draw ref line
  494. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x30));
  495. nvgStrokeWidth(vg, 1);
  496. {
  497. nvgBeginPath(vg);
  498. nvgMoveTo(vg, 0, height/2);
  499. nvgLineTo(vg, width, height/2);
  500. nvgClosePath(vg);
  501. }
  502. nvgStroke(vg);
  503. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x30));
  504. nvgStrokeWidth(vg, 1);
  505. {
  506. nvgBeginPath(vg);
  507. nvgMoveTo(vg, 0, 3*height*0.5f+10);
  508. nvgLineTo(vg, width, 3*height*0.5f+10);
  509. nvgClosePath(vg);
  510. }
  511. nvgStroke(vg);
  512. if ((!module->loading) && (vL.size()>0)) {
  513. // Draw loop
  514. nvgFillColor(vg, nvgRGBA(255, 255, 255, 60));
  515. nvgStrokeWidth(vg, 1);
  516. {
  517. nvgBeginPath(vg);
  518. nvgMoveTo(vg, (module->sampleStart + module->fadeLenght) * zoomWidth / nbSample + zoomLeftAnchor, 0);
  519. nvgLineTo(vg, module->sampleStart * zoomWidth / vL.size() + zoomLeftAnchor, 2*height+10);
  520. nvgLineTo(vg, (module->sampleStart + module->loopLength) * zoomWidth / nbSample + zoomLeftAnchor, 2*height+10);
  521. nvgLineTo(vg, (module->sampleStart + module->loopLength - module->fadeLenght) * zoomWidth / nbSample + zoomLeftAnchor, 0);
  522. nvgLineTo(vg, (module->sampleStart + module->fadeLenght) * zoomWidth / nbSample + zoomLeftAnchor, 0);
  523. nvgClosePath(vg);
  524. }
  525. nvgFill(vg);
  526. //draw selected
  527. if ((module->selected >= 0) && ((size_t)module->selected < s.size())) {
  528. nvgStrokeColor(vg, RED_BIDOO);
  529. {
  530. nvgScissor(vg, 0, 0, width, 2*height+10);
  531. nvgBeginPath(vg);
  532. nvgStrokeWidth(vg, 4);
  533. nvgMoveTo(vg, (s[module->selected] * zoomWidth / nbSample) + zoomLeftAnchor , 2*height+9);
  534. if ((size_t)module->selected < (s.size()-1))
  535. nvgLineTo(vg, (s[module->selected+1] * zoomWidth / nbSample) + zoomLeftAnchor , 2*height+9);
  536. else
  537. nvgLineTo(vg, zoomWidth + zoomLeftAnchor, 2*height+9);
  538. nvgClosePath(vg);
  539. }
  540. nvgStroke(vg);
  541. nvgResetScissor(vg);
  542. }
  543. // Draw waveform
  544. if (nbSample>0) {
  545. nvgStrokeColor(vg, PINK_BIDOO);
  546. nvgSave(vg);
  547. Rect b = Rect(Vec(zoomLeftAnchor, 0), Vec(zoomWidth, height));
  548. nvgScissor(vg, 0, b.pos.y, width, height);
  549. float invNbSample = 1.0f / nbSample;
  550. nvgBeginPath(vg);
  551. for (size_t i = 0; i < vL.size(); i++) {
  552. float x, y;
  553. x = (float)i * invNbSample ;
  554. y = vL[i] * 0.5f + 0.5f;
  555. Vec p;
  556. p.x = b.pos.x + b.size.x * x;
  557. p.y = b.pos.y + b.size.y * (1.0f - y);
  558. if (i == 0) {
  559. nvgMoveTo(vg, p.x, p.y);
  560. }
  561. else {
  562. nvgLineTo(vg, p.x, p.y);
  563. }
  564. }
  565. //nvgClosePath(vg);
  566. nvgLineCap(vg, NVG_MITER);
  567. nvgStrokeWidth(vg, 1);
  568. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  569. nvgStroke(vg);
  570. b = Rect(Vec(zoomLeftAnchor, height+10), Vec(zoomWidth, height));
  571. nvgScissor(vg, 0, b.pos.y, width, height);
  572. nvgBeginPath(vg);
  573. for (size_t i = 0; i < vR.size(); i++) {
  574. float x, y;
  575. x = (float)i * invNbSample;
  576. y = vR[i] * 0.5f + 0.5f;
  577. Vec p;
  578. p.x = b.pos.x + b.size.x * x;
  579. p.y = b.pos.y + b.size.y * (1.0f - y);
  580. if (i == 0)
  581. nvgMoveTo(vg, p.x, p.y);
  582. else {
  583. nvgLineTo(vg, p.x, p.y);
  584. }
  585. }
  586. //nvgClosePath(vg);
  587. nvgLineCap(vg, NVG_MITER);
  588. nvgStrokeWidth(vg, 1);
  589. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  590. nvgStroke(vg);
  591. nvgResetScissor(vg);
  592. }
  593. //draw slices
  594. if (floor(module->params[CANARD::MODE_PARAM].value) == 1) {
  595. nvgScissor(vg, 0, 0, width, 2*height+10);
  596. for (size_t i = 0; i < s.size(); i++) {
  597. if (s[i] != module->deleteSliceMarker) {
  598. nvgStrokeColor(vg, YELLOW_BIDOO);
  599. }
  600. else {
  601. nvgStrokeColor(vg, RED_BIDOO);
  602. }
  603. nvgStrokeWidth(vg, 1);
  604. {
  605. nvgBeginPath(vg);
  606. nvgMoveTo(vg, s[i] * zoomWidth / nbSample + zoomLeftAnchor , 0);
  607. nvgLineTo(vg, s[i] * zoomWidth / nbSample + zoomLeftAnchor , 2*height+10);
  608. nvgClosePath(vg);
  609. }
  610. nvgStroke(vg);
  611. }
  612. nvgResetScissor(vg);
  613. }
  614. nvgRestore(vg);
  615. }
  616. }
  617. };
  618. CANARDWidget::CANARDWidget(CANARD *module) : ModuleWidget(module) {
  619. setPanel(SVG::load(assetPlugin(plugin, "res/CANARD.svg")));
  620. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  621. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  622. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  623. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  624. {
  625. CANARDDisplay *display = new CANARDDisplay();
  626. display->module = module;
  627. display->box.pos = Vec(10, 35);
  628. display->box.size = Vec(175, 110);
  629. addChild(display);
  630. }
  631. static const float portX0[5] = {16, 53, 90, 126, 161};
  632. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(portX0[0]-10, 167), module, CANARD::REC_LIGHT));
  633. addParam(ParamWidget::create<BlueCKD6>(Vec(portX0[0]-6, 170), module, CANARD::RECORD_PARAM, 0.0f, 1.0f, 0.0f));
  634. addParam(ParamWidget::create<BidooBlueKnob>(Vec(portX0[2]-7, 170), module, CANARD::SAMPLE_START_PARAM, 0.0f, 10.0f, 0.0f));
  635. addParam(ParamWidget::create<BidooBlueKnob>(Vec(portX0[3]-7, 170), module, CANARD::LOOP_LENGTH_PARAM, 0.0f, 10.0f, 10.0f));
  636. addParam(ParamWidget::create<BidooBlueKnob>(Vec(portX0[4]-7, 170), module, CANARD::READ_MODE_PARAM, 0.0f, 2.0f, 0.0f));
  637. addInput(Port::create<PJ301MPort>(Vec(portX0[0]-4, 202), Port::INPUT, module, CANARD::RECORD_INPUT));
  638. addInput(Port::create<PJ301MPort>(Vec(portX0[1]-4, 202), Port::INPUT, module, CANARD::TRIG_INPUT));
  639. addInput(Port::create<PJ301MPort>(Vec(portX0[1]-4, 172), Port::INPUT, module, CANARD::GATE_INPUT));
  640. addInput(Port::create<PJ301MPort>(Vec(portX0[2]-4, 202), Port::INPUT, module, CANARD::SAMPLE_START_INPUT));
  641. addInput(Port::create<PJ301MPort>(Vec(portX0[3]-4, 202), Port::INPUT, module, CANARD::LOOP_LENGTH_INPUT));
  642. addInput(Port::create<PJ301MPort>(Vec(portX0[4]-4, 202), Port::INPUT, module, CANARD::READ_MODE_INPUT));
  643. addParam(ParamWidget::create<BidooBlueKnob>(Vec(portX0[0]-7, 245), module, CANARD::SPEED_PARAM, -4.0f, 4.0f, 1.0f));
  644. addParam(ParamWidget::create<BidooBlueKnob>(Vec(portX0[1]-7, 245), module, CANARD::FADE_PARAM, 0.0f, 10.0f, 0.0f));
  645. addParam(ParamWidget::create<BidooBlueKnob>(Vec(portX0[2]-7, 245), module, CANARD::SLICE_PARAM, 0.0f, 10.0f, 0.0f));
  646. addParam(ParamWidget::create<BlueCKD6>(Vec(portX0[3]-6, 245), module, CANARD::CLEAR_PARAM, 0.0f, 1.0f, 0.0f));
  647. addOutput(Port::create<PJ301MPort>(Vec(portX0[4]-4, 247), Port::OUTPUT, module, CANARD::EOC_OUTPUT));
  648. addInput(Port::create<PJ301MPort>(Vec(portX0[0]-4, 277), Port::INPUT, module, CANARD::SPEED_INPUT));
  649. addInput(Port::create<PJ301MPort>(Vec(portX0[1]-4, 277), Port::INPUT, module, CANARD::FADE_INPUT));
  650. addInput(Port::create<PJ301MPort>(Vec(portX0[2]-4, 277), Port::INPUT, module, CANARD::SLICE_INPUT));
  651. addInput(Port::create<PJ301MPort>(Vec(portX0[3]-4, 277), Port::INPUT, module, CANARD::CLEAR_INPUT));
  652. addParam(ParamWidget::create<BidooBlueTrimpot>(Vec(portX0[4]-1, 280), module, CANARD::THRESHOLD_PARAM, 0.01f, 10.0f, 1.0f));
  653. addParam(ParamWidget::create<CKSS>(Vec(90, 325), module, CANARD::MODE_PARAM, 0.0f, 1.0f, 0.0f));
  654. addInput(Port::create<TinyPJ301MPort>(Vec(19, 331), Port::INPUT, module, CANARD::INL_INPUT));
  655. addInput(Port::create<TinyPJ301MPort>(Vec(19+24, 331), Port::INPUT, module, CANARD::INR_INPUT));
  656. addOutput(Port::create<TinyPJ301MPort>(Vec(138, 331), Port::OUTPUT, module, CANARD::OUTL_OUTPUT));
  657. addOutput(Port::create<TinyPJ301MPort>(Vec(138+24, 331), Port::OUTPUT, module, CANARD::OUTR_OUTPUT));
  658. }
  659. struct CANARDDeleteSlice : MenuItem {
  660. CANARDWidget *canardWidget;
  661. CANARD *canardModule;
  662. void onAction(EventAction &e) override {
  663. canardModule->deleteFlag = true;
  664. }
  665. };
  666. struct CANARDDeleteSliceMarker : MenuItem {
  667. CANARDWidget *canardWidget;
  668. CANARD *canardModule;
  669. void onAction(EventAction &e) override {
  670. canardModule->deleteSliceMarkerFlag = true;
  671. }
  672. };
  673. struct CANARDAddSliceMarker : MenuItem {
  674. CANARDWidget *canardWidget;
  675. CANARD *canardModule;
  676. void onAction(EventAction &e) override {
  677. canardModule->addSliceMarkerFlag = true;
  678. }
  679. };
  680. struct CANARDTransientDetect : MenuItem {
  681. CANARDWidget *canardWidget;
  682. CANARD *canardModule;
  683. void onAction(EventAction &e) override {
  684. canardModule->slices.clear();
  685. canardModule->slices.push_back(0);
  686. unsigned int i = 0;
  687. unsigned int size = 256;
  688. vector<float>::const_iterator first;
  689. vector<float>::const_iterator last;
  690. float prevNrgy = 0.0f;
  691. while (i+size<canardModule->totalSampleCount) {
  692. first = canardModule->playBuffer[0].begin() + i;
  693. last = canardModule->playBuffer[0].begin() + i + size;
  694. vector<float> newVec(first, last);
  695. float nrgy = 0.0f;
  696. float zcRate = 0.0f;
  697. unsigned int zcIdx = 0;
  698. bool first = true;
  699. for (unsigned int k = 0; k < size; k++) {
  700. nrgy += 100*newVec[k]*newVec[k]/size;
  701. if (newVec[k]==0.0f) {
  702. zcRate += 1;
  703. if (first) {
  704. zcIdx = k;
  705. first = false;
  706. }
  707. }
  708. }
  709. if ((nrgy > canardModule->params[CANARD::THRESHOLD_PARAM].value) && (nrgy > 10*prevNrgy))
  710. canardModule->slices.push_back(i+zcIdx);
  711. i+=size;
  712. prevNrgy = nrgy;
  713. }
  714. }
  715. };
  716. struct CANARDLoadSample : MenuItem {
  717. CANARDWidget *canardWidget;
  718. CANARD *canardModule;
  719. void onAction(EventAction &e) override {
  720. std::string dir = canardModule->lastPath.empty() ? assetLocal("") : stringDirectory(canardModule->lastPath);
  721. char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL);
  722. if (path) {
  723. canardModule->loadSample(path);
  724. free(path);
  725. }
  726. }
  727. };
  728. struct CANARDSaveSample : MenuItem {
  729. CANARDWidget *canardWidget;
  730. CANARD *canardModule;
  731. void onAction(EventAction &e) override {
  732. std::string dir = canardModule->lastPath.empty() ? assetLocal("") : stringDirectory(canardModule->lastPath);
  733. std::string fileName = canardModule->waveFileName.empty() ? "temp.wav" : canardModule->waveFileName;
  734. char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), (fileName).c_str(), NULL);
  735. if (path) {
  736. canardModule->lastPath = path;
  737. canardModule->waveFileName = stringDirectory(path);
  738. canardModule->waveExtension = stringExtension(path);
  739. drwav_data_format format;
  740. format.container = drwav_container_riff;
  741. format.format = DR_WAVE_FORMAT_PCM;
  742. format.channels = 2;
  743. format.sampleRate = engineGetSampleRate();
  744. format.bitsPerSample = 32;
  745. drwav* pWav = drwav_open_file_write(path, &format);
  746. int *pSamples = new int[2*canardModule->totalSampleCount];
  747. for (unsigned int i = 0; i < canardModule->totalSampleCount; i++) {
  748. pSamples[2*i]= floor(canardModule->playBuffer[0][i]*2147483647);
  749. pSamples[2*i+1]= floor(canardModule->playBuffer[1][i]*2147483647);
  750. }
  751. drwav_write(pWav, 2*canardModule->totalSampleCount, pSamples);
  752. drwav_close(pWav);
  753. free(path);
  754. delete [] pSamples;
  755. }
  756. }
  757. };
  758. Menu *CANARDWidget::createContextMenu() {
  759. CANARDWidget *canardWidget = dynamic_cast<CANARDWidget*>(this);
  760. assert(canardWidget);
  761. CANARD *canardModule = dynamic_cast<CANARD*>(module);
  762. assert(canardModule);
  763. Menu *menu = ModuleWidget::createContextMenu();
  764. MenuLabel *spacerLabel;
  765. if ((canardModule->selected>=0) || (canardModule->totalSampleCount>0)) {
  766. spacerLabel = new MenuLabel();
  767. menu->addChild(spacerLabel);
  768. }
  769. if (canardModule->selected>=0) {
  770. CANARDDeleteSlice *deleteItem = new CANARDDeleteSlice();
  771. deleteItem->text = "Delete slice";
  772. deleteItem->canardWidget = this;
  773. deleteItem->canardModule = canardModule;
  774. menu->addChild(deleteItem);
  775. }
  776. if (canardModule->totalSampleCount>0) {
  777. CANARDAddSliceMarker *addSliceItem = new CANARDAddSliceMarker();
  778. addSliceItem->text = "Add slice marker";
  779. addSliceItem->canardWidget = this;
  780. addSliceItem->canardModule = canardModule;
  781. menu->addChild(addSliceItem);
  782. CANARDDeleteSliceMarker *deleteSliceItem = new CANARDDeleteSliceMarker();
  783. deleteSliceItem->text = "Delete slice marker";
  784. deleteSliceItem->canardWidget = this;
  785. deleteSliceItem->canardModule = canardModule;
  786. menu->addChild(deleteSliceItem);
  787. CANARDTransientDetect *trnsientItem = new CANARDTransientDetect();
  788. trnsientItem->text = "Search transients";
  789. trnsientItem->canardWidget = this;
  790. trnsientItem->canardModule = canardModule;
  791. menu->addChild(trnsientItem);
  792. }
  793. spacerLabel = new MenuLabel();
  794. menu->addChild(spacerLabel);
  795. CANARDLoadSample *loadItem = new CANARDLoadSample();
  796. loadItem->text = "Load sample";
  797. loadItem->canardWidget = this;
  798. loadItem->canardModule = canardModule;
  799. menu->addChild(loadItem);
  800. CANARDSaveSample *saveItem = new CANARDSaveSample();
  801. saveItem->text = "Save sample";
  802. saveItem->canardWidget = this;
  803. saveItem->canardModule = canardModule;
  804. menu->addChild(saveItem);
  805. return menu;
  806. }
  807. } // namespace rack_plugin_Bidoo
  808. using namespace rack_plugin_Bidoo;
  809. RACK_PLUGIN_MODEL_INIT(Bidoo, CANARD) {
  810. Model *modelCANARD = Model::create<CANARD, CANARDWidget>("Bidoo","cANARd", "cANARd sampler", SAMPLER_TAG, GRANULAR_TAG, RECORDING_TAG);
  811. return modelCANARD;
  812. }