Audio plugin host https://kx.studio/carla
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.

381 lines
8.9KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. BankView.cpp - View Of Bank Fields
  4. Copyright (C) 2016 Mark McCurry
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. */
  10. #include "BankView.h"
  11. #include "../Misc/Util.h"
  12. #include <FL/Fl.H>
  13. #include <FL/Fl_Check_Button.H>
  14. #include <FL/fl_ask.H>
  15. #include <rtosc/rtosc.h>
  16. #include <cstdio>
  17. #include <cstring>
  18. #include <cassert>
  19. BankList::BankList(int x,int y, int w, int h, const char *label)
  20. :Fl_Osc_Choice(x,y,w,h,label)
  21. {}
  22. void BankList::init(std::string path)
  23. {
  24. ext = path;
  25. oscRegister("bank/bank_select");
  26. oscRegister(path.c_str());
  27. oscWrite("bank/banks", "");
  28. }
  29. void BankList::OSC_raw(const char *msg)
  30. {
  31. if(!strcmp(msg, "/bank/bank_select") && !strcmp(rtosc_argument_string(msg),"iss")) {
  32. const int pos = rtosc_argument(msg, 0).i;
  33. const char *path = rtosc_argument(msg, 1).s;
  34. value(0);
  35. if(pos == 0)
  36. this->clear();
  37. if(pos <= this->size()-2) {
  38. return;
  39. }
  40. this->add(path);
  41. }
  42. if(!strcmp(msg, "/bank/bank_select")&& !strcmp(rtosc_argument_string(msg),"i")) {
  43. int val = rtosc_argument(msg, 0).i;
  44. if(value() != val) {
  45. value(val);
  46. for(int i=0; i<160; ++i)
  47. osc->write("/bank/slot"+to_s(i), "");
  48. }
  49. }
  50. }
  51. BankSlot::BankSlot(int x,int y, int w, int h, const char *label)
  52. :Fl_Button(x,y,w,h,label), nslot(-1)
  53. {
  54. memset(labelstr, 0, sizeof(labelstr));
  55. box(FL_THIN_UP_BOX);
  56. labelfont(0);
  57. labelsize(13);
  58. align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
  59. }
  60. int BankSlot::handle(int event)
  61. {
  62. int what = 0;
  63. if (Fl::event_inside(this))
  64. {
  65. what=0;
  66. if ((event==FL_RELEASE)&&(Fl::event_button()==1))
  67. what=1;
  68. if ((event==FL_RELEASE)&&(Fl::event_button()==3))
  69. what=2;
  70. }
  71. int tmp=Fl_Button::handle(event);
  72. if (what && Fl::event_inside(this))
  73. bv->react(what, nslot);
  74. return tmp;
  75. }
  76. void BankSlot::init(int nslot_, BankView *bv_)
  77. {
  78. nslot = nslot_;
  79. bv = bv_;
  80. snprintf(labelstr, 127, "%d.", nslot_);
  81. label(labelstr);
  82. }
  83. void BankSlot::update(const char *name__, const char *fname__)
  84. {
  85. name_ = name__;
  86. filename_ = fname__;
  87. snprintf(labelstr, 127, "%d. %s", nslot, name_.c_str());
  88. label(labelstr);
  89. if(name_.empty())
  90. label("");
  91. color(empty() ? 46 : 51);
  92. #ifdef NTK_GUI
  93. redraw();
  94. #endif
  95. }
  96. bool BankSlot::empty(void) const
  97. {
  98. return filename_.empty();
  99. }
  100. const char *BankSlot::name(void) const
  101. {
  102. return name_.c_str();
  103. }
  104. const char *BankSlot::filename(void) const
  105. {
  106. return filename_.c_str();
  107. }
  108. /*
  109. void BankSlot::init(int nslot_, int *what_, int *whatslot_,void (BankProcess_:: *fnc_)(void),BankProcess_ *bp_,Bank *bank_,int *nselected_) {
  110. nslot=nslot_;
  111. what=what_;
  112. whatslot=whatslot_;
  113. fnc=fnc_;
  114. bp=bp_;
  115. //bank=bank_;
  116. nselected=nselected_;
  117. box(FL_THIN_UP_BOX);
  118. labelfont(0);
  119. labelsize(13);
  120. align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_CLIP);
  121. highlight=0;
  122. //refresh();
  123. }
  124. */
  125. /*
  126. void BankSlot::refresh() {
  127. if (bank->emptyslot(nslot))
  128. color(46);
  129. else if (bank->isPADsynth_used(nslot))
  130. color(26);
  131. else
  132. color(51);
  133. if (*nselected==nslot)
  134. color(6);
  135. copy_label(bank->getnamenumbered(nslot).c_str());
  136. }
  137. */
  138. static int modeCb(const char *label)
  139. {
  140. if(!strcmp("Read", label))
  141. return 1;
  142. else if(!strcmp("Write", label))
  143. return 2;
  144. else if(!strcmp("Clear", label))
  145. return 3;
  146. else if(!strcmp("Swap", label))
  147. return 4;
  148. return -1;
  149. }
  150. static void modeButtonCb(Fl_Widget *w, void *v)
  151. {
  152. BankViewControls *bvc = (BankViewControls*)v;
  153. bvc->mode(modeCb(w->label()));
  154. }
  155. BankViewControls::BankViewControls(int x, int y, int w, int h, const char *label)
  156. :Fl_Group(x,y,w,h,label)
  157. {
  158. //Margin
  159. const int m = 10;
  160. //Width per elm
  161. const float W = w/4;
  162. read = new Fl_Check_Button(x+m+0*W, y+m, W-2*m, h-2*m, "Read");
  163. write = new Fl_Check_Button(x+m+1*W, y+m, W-2*m, h-2*m, "Write");
  164. clear = new Fl_Check_Button(x+m+2*W, y+m, W-2*m, h-2*m, "Clear");
  165. swap = new Fl_Check_Button(x+m+3*W, y+m, W-2*m, h-2*m, "Swap");
  166. read->box(FL_BORDER_BOX);
  167. write->box(FL_BORDER_BOX);
  168. clear->box(FL_BORDER_BOX);
  169. swap->box(FL_BORDER_BOX);
  170. read->callback(modeButtonCb, this);
  171. write->callback(modeButtonCb, this);
  172. clear->callback(modeButtonCb, this);
  173. swap->callback(modeButtonCb, this);
  174. mode(1);
  175. }
  176. int BankViewControls::mode(void) const
  177. {
  178. return mode_;
  179. }
  180. void BankViewControls::mode(int m)
  181. {
  182. mode_ = m;
  183. int M = m-1;
  184. assert(0 <= M && M <= 3);
  185. Fl_Button *buttons[4]{read, write, clear, swap};
  186. for(int i=0; i<4; ++i)
  187. buttons[i]->value(i==M);
  188. }
  189. BankView::BankView(int x,int y, int w, int h, const char *label)
  190. :Fl_Group(x,y,w,h,label), bvc(NULL), slots{0}, osc(0),
  191. loc(""), nselected(-1), npart(0), cbwig_(0)
  192. {}
  193. BankView::~BankView(void)
  194. {
  195. if(osc)
  196. osc->removeLink("/bankview", this);
  197. }
  198. void BankView::init(Fl_Osc_Interface *osc_, BankViewControls *bvc_, int *npart_)
  199. {
  200. assert(osc_);
  201. osc = osc_;
  202. bvc = bvc_;
  203. npart = npart_;
  204. osc->createLink("/bankview", this);
  205. //Element Size
  206. const float width = w()/5.0;
  207. const float height = h()/32.0;
  208. //Offsets
  209. const int X = x();
  210. const int Y = y();
  211. begin();
  212. //Place All Slots
  213. for(int i=0; i<5; ++i)
  214. for(int j=0; j<32; ++j)
  215. slots[i*32 + j] =
  216. new BankSlot(X + i*width, Y + j*height, width, height);
  217. end();
  218. //Initialize callbacks
  219. for(int i=0; i<160; ++i)
  220. slots[i]->init(i, this);
  221. //Create Slot Listeners
  222. for(int i=0; i<160; ++i)
  223. osc->createLink("/bank/slot"+to_s(i), this);
  224. //Request Values
  225. for(int i=0; i<160; ++i)
  226. osc->write("/bank/slot"+to_s(i), "");
  227. }
  228. /*
  229. * React to user input.
  230. * This consists of the events:
  231. * - Rename Slot (right click)
  232. * - Read From Slot
  233. * - Write To Slot
  234. * - Swap Slot First Selection
  235. * - Swap Slot Second Selction
  236. *
  237. * TODO restore autoclose functionality
  238. */
  239. void BankView::react(int event, int nslot)
  240. {
  241. BankSlot &slot = *slots[nslot];
  242. const bool isempty = slot.empty();
  243. const int mode = bvc->mode();
  244. //Rename slot
  245. if (event==2 && !isempty && mode!=4) {
  246. if(const char *name=fl_input("Slot (instrument) name:", slot.name())) {
  247. osc->write("/bank/rename_slot", "is", nslot, name);
  248. osc->write("/bank/slot"+to_s(nslot), "");
  249. }
  250. }
  251. //Reads from slot
  252. if ((event==1)&&(mode==1) && !isempty){
  253. printf("Loading a part #%d with file '%s'\n", nslot, slot.filename());
  254. osc->write("/load-part", "iss", *npart, slot.filename(),
  255. slot.name());
  256. osc->writeValue("/part"+to_s(*npart)+"/Pname", slot.name());
  257. if(cbwig_)
  258. cbwig_->do_callback();
  259. }
  260. //save(write) to slot
  261. if(event==1 && mode==2){
  262. if(isempty ||
  263. fl_choice("Overwrite the slot no. %d ?","No","Yes",NULL,nslot+1)) {
  264. osc->write("/bank/save_to_slot", "ii", *npart, nslot);
  265. osc->write("/bank/slot"+to_s(nslot), "");
  266. }
  267. bvc->mode(1);
  268. }
  269. //Clears the slot
  270. if(event==1 && mode==3) {
  271. if (!isempty &&
  272. fl_choice("Clear the slot no. %d ?","No","Yes",NULL, nslot+1)) {
  273. osc->write("/bank/clear_slot", "i", nslot);
  274. osc->write("/bank/slot"+to_s(nslot), "");
  275. }
  276. bvc->mode(1);
  277. }
  278. //Swap
  279. if(mode==4) {
  280. if(event==1 && nselected>=0){
  281. osc->write("/bank/swap_slots", "ii", nselected, nslot);
  282. osc->write("/bank/slot"+to_s(nslot), "");
  283. osc->write("/bank/slot"+to_s(nselected), "");
  284. nselected=-1;
  285. } else if(nselected<0 || event==2) {
  286. nselected=nslot;
  287. };
  288. };
  289. }
  290. void BankView::OSC_raw(const char *msg)
  291. {
  292. if(!strcmp(rtosc_argument_string(msg), "iss")) {
  293. int nslot = rtosc_argument(msg,0).i;
  294. const char *name = rtosc_argument(msg,1).s;
  295. const char *fname = rtosc_argument(msg,2).s;
  296. if(0 <= nslot && nslot < 160)
  297. slots[nslot]->update(name, fname);
  298. } if(!strcmp(rtosc_argument_string(msg), "ss")) {
  299. while(*msg && !isdigit(*msg)) msg++;
  300. int nslot = atoi(msg);
  301. const char *name = rtosc_argument(msg,0).s;
  302. const char *fname = rtosc_argument(msg,1).s;
  303. if(0 <= nslot && nslot < 160)
  304. slots[nslot]->update(name, fname);
  305. }
  306. }
  307. void BankView::cbwig(Fl_Widget *w)
  308. {
  309. cbwig_ = w;
  310. }
  311. void BankView::refresh(void)
  312. {
  313. assert(osc);
  314. //Odd case during initialization
  315. if(!osc)
  316. return;
  317. for(int i=0; i<160; ++i)
  318. osc->write("/bank/slot"+to_s(i), "");
  319. }