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.

447 lines
14KB

  1. #include "cf.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "osdialog.h"
  4. #include "AudioFile.h"
  5. #include <vector>
  6. #include "cmath"
  7. #ifdef _MSC_VER
  8. #include "dirent_win32/dirent.h"
  9. #else
  10. #include <dirent.h>
  11. #endif
  12. #include <algorithm> //----added by Joakim Lindbom
  13. using namespace std;
  14. namespace rack_plugin_cf {
  15. struct PLAYER : Module {
  16. enum ParamIds {
  17. PLAY_PARAM,
  18. POS_PARAM,
  19. LSTART_PARAM,
  20. LSPEED_PARAM,
  21. TSTART_PARAM,
  22. TSPEED_PARAM,
  23. SPD_PARAM,
  24. NEXT_PARAM,
  25. PREV_PARAM,
  26. OSC_PARAM,
  27. NUM_PARAMS
  28. };
  29. enum InputIds {
  30. GATE_INPUT,
  31. POS_INPUT,
  32. SPD_INPUT,
  33. PREV_INPUT,
  34. NEXT_INPUT,
  35. TRIG_INPUT,
  36. NUM_INPUTS
  37. };
  38. enum OutputIds {
  39. OUT_OUTPUT,
  40. OUT2_OUTPUT,
  41. NUM_OUTPUTS
  42. };
  43. enum LightIds {
  44. OSC_LIGHT,
  45. NUM_LIGHTS
  46. };
  47. bool play = false;
  48. string lastPath = "";
  49. AudioFile<double> audioFile;
  50. float samplePos = 0;
  51. float startPos = 0;
  52. vector<double> displayBuff;
  53. string fileDesc;
  54. bool fileLoaded = false;
  55. SchmittTrigger playTrigger;
  56. SchmittTrigger playGater;
  57. SchmittTrigger nextTrigger;
  58. SchmittTrigger prevTrigger;
  59. SchmittTrigger nextinTrigger;
  60. SchmittTrigger previnTrigger;
  61. SchmittTrigger oscTrigger;
  62. vector <string> fichier;
  63. int sampnumber = 0;
  64. int retard = 0;
  65. bool reload = false ;
  66. bool oscState = false ;
  67. PLAYER() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { }
  68. void step() override;
  69. void loadSample(std::string path);
  70. // persistence
  71. json_t *toJson() override {
  72. json_t *rootJ = json_object();
  73. // lastPath
  74. json_object_set_new(rootJ, "lastPath", json_string(lastPath.c_str()));
  75. json_object_set_new(rootJ, "oscstate", json_integer(oscState));
  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. reload = true ;
  84. loadSample(lastPath);
  85. }
  86. json_t *oscstateJ = json_object_get(rootJ, "oscstate");
  87. if (oscstateJ) {
  88. oscState = json_integer_value(oscstateJ);
  89. }
  90. lights[OSC_LIGHT].value=oscState;
  91. }
  92. };
  93. void PLAYER::loadSample(std::string path) {
  94. if (audioFile.load (path.c_str())) {
  95. fileLoaded = true;
  96. vector<double>().swap(displayBuff);
  97. for (int i=0; i < audioFile.getNumSamplesPerChannel(); i = i + floor(audioFile.getNumSamplesPerChannel()/130)) {
  98. displayBuff.push_back(audioFile.samples[0][i]);
  99. }
  100. fileDesc = stringFilename(path)+ "\n";
  101. fileDesc += std::to_string(audioFile.getSampleRate())+ " Hz" + " - "; //"\n";
  102. fileDesc += std::to_string(audioFile.getBitDepth())+ " bits" + " \n";
  103. // fileDesc += std::to_string(audioFile.getNumSamplesPerChannel())+ " smp" +"\n";
  104. // fileDesc += std::to_string(audioFile.getLengthInSeconds())+ " s." + "\n";
  105. fileDesc += std::to_string(audioFile.getNumChannels())+ " channel(s)" + "\n";
  106. // fileDesc += std::to_string(audioFile.isMono())+ "\n";
  107. // fileDesc += std::to_string(audioFile.isStereo())+ "\n";
  108. if (reload) {
  109. DIR* rep = NULL;
  110. struct dirent* dirp = NULL;
  111. std::string dir = path.empty() ? assetLocal("") : stringDirectory(path);
  112. rep = opendir(dir.c_str());
  113. int i = 0;
  114. fichier.clear();
  115. while ((dirp = readdir(rep)) != NULL) {
  116. std::string name = dirp->d_name;
  117. std::size_t found = name.find(".wav",name.length()-5);
  118. if (found==std::string::npos) found = name.find(".WAV",name.length()-5);
  119. if (found==std::string::npos) found = name.find(".aif",name.length()-5);
  120. if (found==std::string::npos) found = name.find(".AIF",name.length()-5);
  121. if (found==std::string::npos) found = name.find(".aiff",name.length()-5);
  122. if (found==std::string::npos) found = name.find(".AIFF",name.length()-5);
  123. if (found!=std::string::npos) {
  124. fichier.push_back(name);
  125. if ((dir + "/" + name)==path) {sampnumber = i;}
  126. i=i+1;
  127. }
  128. }
  129. //----added by Joakim Lindbom
  130. sort(fichier.begin(), fichier.end()); // Linux needs this to get files in right order
  131. for (int o=0;o<int(fichier.size()-1); o++) {
  132. if ((dir + "/" + fichier[o])==path) {
  133. sampnumber = o;
  134. }
  135. }
  136. //---------------
  137. closedir(rep);
  138. reload = false;
  139. }
  140. lastPath = path;
  141. }
  142. else {
  143. fileLoaded = false;
  144. }
  145. }
  146. void PLAYER::step() {
  147. if (fileLoaded) {
  148. if (nextTrigger.process(params[NEXT_PARAM].value)+nextinTrigger.process(inputs[NEXT_INPUT].value))
  149. {
  150. std::string dir = lastPath.empty() ? assetLocal("") : stringDirectory(lastPath);
  151. if (sampnumber < int(fichier.size()-1)) sampnumber=sampnumber+1; else sampnumber =0;
  152. loadSample(dir + "/" + fichier[sampnumber]);
  153. }
  154. if (prevTrigger.process(params[PREV_PARAM].value)+previnTrigger.process(inputs[PREV_INPUT].value))
  155. {retard = 1000;
  156. std::string dir = lastPath.empty() ? assetLocal("") : stringDirectory(lastPath);
  157. if (sampnumber > 0) sampnumber=sampnumber-1; else sampnumber =int(fichier.size()-1);
  158. loadSample(dir + "/" + fichier[sampnumber]);
  159. }
  160. } else fileDesc = "right click to load \n .wav or .aif sample \n :)";
  161. if (oscTrigger.process(params[OSC_PARAM].value))
  162. {oscState =!oscState;lights[OSC_LIGHT].value=oscState;}
  163. // Play
  164. if (!oscState) {
  165. bool gated = inputs[GATE_INPUT].value > 0;
  166. if (inputs[POS_INPUT].active)
  167. startPos = clamp((params[LSTART_PARAM].value + inputs[POS_INPUT].value * params[TSTART_PARAM].value),0.0f,10.0f)*audioFile.getNumSamplesPerChannel()/10;
  168. else {startPos = clamp((params[LSTART_PARAM].value),0.0f,10.0f)*audioFile.getNumSamplesPerChannel()/10;
  169. inputs[POS_INPUT].value = 0 ;
  170. }
  171. if (!inputs[TRIG_INPUT].active) {
  172. if (playGater.process(inputs[GATE_INPUT].value)) {
  173. play = true;
  174. samplePos = startPos;
  175. }
  176. } else {
  177. if (playTrigger.process(inputs[TRIG_INPUT].value)) {
  178. play = true;
  179. samplePos = startPos;
  180. }
  181. }
  182. if ((play) && ((floor(samplePos) < audioFile.getNumSamplesPerChannel()) && (floor(samplePos) >= 0))) {
  183. if (audioFile.getNumChannels() == 1) {
  184. outputs[OUT_OUTPUT].value = 5 * audioFile.samples[0][floor(samplePos)];
  185. outputs[OUT2_OUTPUT].value = 5 * audioFile.samples[0][floor(samplePos)];}
  186. else if (audioFile.getNumChannels() ==2) {
  187. outputs[OUT_OUTPUT].value = 5 * audioFile.samples[0][floor(samplePos)];
  188. outputs[OUT2_OUTPUT].value = 5 * audioFile.samples[1][floor(samplePos)];
  189. }
  190. if (inputs[SPD_INPUT].active)
  191. samplePos = samplePos+1+(params[LSPEED_PARAM].value +inputs[SPD_INPUT].value * params[TSPEED_PARAM].value) /3;
  192. else {
  193. samplePos = samplePos+1+(params[LSPEED_PARAM].value) /3;
  194. inputs[SPD_INPUT].value = 0 ;}
  195. }
  196. else
  197. {
  198. play = false;
  199. outputs[OUT_OUTPUT].value = 0;outputs[OUT2_OUTPUT].value = 0;
  200. }
  201. if (!inputs[TRIG_INPUT].active) {if (gated == false) {play = false; outputs[OUT_OUTPUT].value = 0;outputs[OUT2_OUTPUT].value = 0;}}
  202. } else {
  203. if (((floor(samplePos) < audioFile.getNumSamplesPerChannel()) && (floor(samplePos) >= 0))) {
  204. if (playTrigger.process(inputs[TRIG_INPUT].value)) samplePos = 0;
  205. if (audioFile.getNumChannels() == 1) {
  206. outputs[OUT_OUTPUT].value = 5 * audioFile.samples[0][floor(samplePos)];
  207. outputs[OUT2_OUTPUT].value = 5 * audioFile.samples[0][floor(samplePos)];}
  208. else if (audioFile.getNumChannels() ==2) {
  209. outputs[OUT_OUTPUT].value = 5 * audioFile.samples[0][floor(samplePos)];
  210. outputs[OUT2_OUTPUT].value = 5 * audioFile.samples[1][floor(samplePos)];
  211. }
  212. if (inputs[SPD_INPUT].active)
  213. samplePos = samplePos+1+(params[LSPEED_PARAM].value +inputs[SPD_INPUT].value * params[TSPEED_PARAM].value) /3;
  214. else {
  215. samplePos = samplePos+1+(params[LSPEED_PARAM].value) /3;
  216. inputs[SPD_INPUT].value = 0 ;}
  217. }
  218. else
  219. {
  220. samplePos=0;
  221. }
  222. }
  223. }
  224. struct upButton : SVGSwitch, MomentarySwitch {
  225. upButton() {
  226. addFrame(SVG::load(assetPlugin(plugin, "res/upButton.svg")));
  227. addFrame(SVG::load(assetPlugin(plugin, "res/upButtonDown.svg")));
  228. sw->wrap();
  229. box.size = sw->box.size;
  230. }
  231. };
  232. struct downButton : SVGSwitch, MomentarySwitch {
  233. downButton() {
  234. addFrame(SVG::load(assetPlugin(plugin, "res/downButton.svg")));
  235. addFrame(SVG::load(assetPlugin(plugin, "res/downButtonDown.svg")));
  236. sw->wrap();
  237. box.size = sw->box.size;
  238. }
  239. };
  240. struct PLAYERDisplay : TransparentWidget {
  241. PLAYER *module;
  242. int frame = 0;
  243. shared_ptr<Font> font;
  244. PLAYERDisplay() {
  245. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  246. }
  247. void draw(NVGcontext *vg) override {
  248. nvgFontSize(vg, 12);
  249. nvgFontFaceId(vg, font->handle);
  250. nvgTextLetterSpacing(vg, -2);
  251. nvgFillColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0xff));
  252. nvgTextBox(vg, 5, 5,120, module->fileDesc.c_str(), NULL);
  253. // Draw ref line
  254. nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x40));
  255. {
  256. nvgBeginPath(vg);
  257. nvgMoveTo(vg, 0, 125);
  258. nvgLineTo(vg, 125, 125);
  259. nvgClosePath(vg);
  260. }
  261. nvgStroke(vg);
  262. if (module->fileLoaded) {
  263. // Draw play line
  264. nvgStrokeColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xff));
  265. nvgStrokeWidth(vg, 0.8);
  266. {
  267. nvgBeginPath(vg);
  268. nvgMoveTo(vg, floor(module->samplePos * 125 / module->audioFile.getNumSamplesPerChannel()) , 85);
  269. nvgLineTo(vg, floor(module->samplePos * 125 / module->audioFile.getNumSamplesPerChannel()) , 165);
  270. nvgClosePath(vg);
  271. }
  272. nvgStroke(vg);
  273. // Draw start line
  274. nvgStrokeColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xff));
  275. nvgStrokeWidth(vg, 1.5);
  276. {
  277. nvgBeginPath(vg);
  278. nvgMoveTo(vg, floor(module->startPos * 125 / module->audioFile.getNumSamplesPerChannel()) , 85);
  279. nvgLineTo(vg, floor(module->startPos * 125 / module->audioFile.getNumSamplesPerChannel()) , 165);
  280. nvgClosePath(vg);
  281. }
  282. nvgStroke(vg);
  283. // Draw waveform
  284. nvgStrokeColor(vg, nvgRGBA(0xe1, 0x02, 0x78, 0xc0));
  285. nvgSave(vg);
  286. Rect b = Rect(Vec(0, 85), Vec(125, 80));
  287. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  288. nvgBeginPath(vg);
  289. for (unsigned int i = 0; i < module->displayBuff.size(); i++) {
  290. float x, y;
  291. x = (float)i / (module->displayBuff.size() - 1);
  292. y = module->displayBuff[i] / 2.0 + 0.5;
  293. Vec p;
  294. p.x = b.pos.x + b.size.x * x;
  295. p.y = b.pos.y + b.size.y * (1.0 - y);
  296. if (i == 0)
  297. nvgMoveTo(vg, p.x, p.y);
  298. else
  299. nvgLineTo(vg, p.x, p.y);
  300. }
  301. nvgLineCap(vg, NVG_ROUND);
  302. nvgMiterLimit(vg, 2.0);
  303. nvgStrokeWidth(vg, 1.5);
  304. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  305. nvgStroke(vg);
  306. nvgResetScissor(vg);
  307. nvgRestore(vg);
  308. }
  309. }
  310. };
  311. struct PLAYERWidget : ModuleWidget {
  312. PLAYERWidget(PLAYER *module);
  313. Menu *createContextMenu() override;
  314. };
  315. PLAYERWidget::PLAYERWidget(PLAYER *module) : ModuleWidget(module) {
  316. setPanel(SVG::load(assetPlugin(plugin, "res/PLAYER.svg")));
  317. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  318. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  319. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  320. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  321. {
  322. PLAYERDisplay *display = new PLAYERDisplay();
  323. display->module = module;
  324. display->box.pos = Vec(5, 40);
  325. display->box.size = Vec(130, 250);
  326. addChild(display);
  327. }
  328. static const float portX0[4] = {10, 40, 70, 100};
  329. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(23, 230), module, PLAYER::LSTART_PARAM, 0.0f, 10.0f, 0.0f));
  330. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(73, 230), module, PLAYER::LSPEED_PARAM, -5.0f, 5.0f, 0.0f));
  331. addParam(ParamWidget::create<Trimpot>(Vec(42, 278), module, PLAYER::TSTART_PARAM, -1.0f, 1.0f, 0.0f));
  332. addParam(ParamWidget::create<Trimpot>(Vec(73, 278), module, PLAYER::TSPEED_PARAM, -1.0f, 1.0f, 0.0f));
  333. addInput(Port::create<PJ301MPort>(Vec(portX0[0], 321), Port::INPUT, module, PLAYER::GATE_INPUT));
  334. addInput(Port::create<PJ301MPort>(Vec(portX0[1], 321), Port::INPUT, module, PLAYER::POS_INPUT));
  335. addInput(Port::create<PJ301MPort>(Vec(portX0[2], 321), Port::INPUT, module, PLAYER::SPD_INPUT));
  336. addOutput(Port::create<PJ301MPort>(Vec(portX0[3], 275), Port::OUTPUT, module, PLAYER::OUT_OUTPUT));
  337. addOutput(Port::create<PJ301MPort>(Vec(portX0[3], 321), Port::OUTPUT, module, PLAYER::OUT2_OUTPUT));
  338. addInput(Port::create<PJ301MPort>(Vec(portX0[0], 91), Port::INPUT, module, PLAYER::PREV_INPUT));
  339. addInput(Port::create<PJ301MPort>(Vec(portX0[3], 91), Port::INPUT, module, PLAYER::NEXT_INPUT));
  340. addInput(Port::create<PJ301MPort>(Vec(portX0[0], 275), Port::INPUT, module, PLAYER::TRIG_INPUT));
  341. addParam(ParamWidget::create<upButton>(Vec(43, 95), module, PLAYER::PREV_PARAM, 0.0f, 1.0f, 0.0f));
  342. addParam(ParamWidget::create<downButton>(Vec(73, 95), module, PLAYER::NEXT_PARAM, 0.0f, 1.0f, 0.0f));
  343. addParam(ParamWidget::create<LEDButton>(Vec(11, 210), module, PLAYER::OSC_PARAM, 0.0, 1.0, 0.0));
  344. addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(15.4, 214.4), module, PLAYER::OSC_LIGHT));
  345. }
  346. struct PLAYERItem : MenuItem {
  347. PLAYER *player;
  348. void onAction(EventAction &e) override {
  349. std::string dir = player->lastPath.empty() ? assetLocal("") : stringDirectory(player->lastPath);
  350. char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL);
  351. if (path) {
  352. player->play = false;
  353. player->reload = true;
  354. player->loadSample(path);
  355. player->samplePos = 0;
  356. player->lastPath = path;
  357. free(path);
  358. }
  359. }
  360. };
  361. Menu *PLAYERWidget::createContextMenu() {
  362. Menu *menu = ModuleWidget::createContextMenu();
  363. MenuLabel *spacerLabel = new MenuLabel();
  364. menu->addChild(spacerLabel);
  365. PLAYER *player = dynamic_cast<PLAYER*>(module);
  366. assert(player);
  367. PLAYERItem *sampleItem = new PLAYERItem();
  368. sampleItem->text = "Load sample";
  369. sampleItem->player = player;
  370. menu->addChild(sampleItem);
  371. return menu;
  372. }
  373. } // namespace rack_plugin_cf
  374. using namespace rack_plugin_cf;
  375. RACK_PLUGIN_MODEL_INIT(cf, PLAYER) {
  376. Model *modelPLAYER = Model::create<PLAYER, PLAYERWidget>("cf", "PLAYER", "Player", SAMPLER_TAG);
  377. return modelPLAYER;
  378. }