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.

automations.cpp 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #include <rtosc/automations.h>
  2. #include <cmath>
  3. using namespace rtosc;
  4. AutomationMgr::AutomationMgr(int slots, int per_slot, int control_points)
  5. :nslots(slots), per_slot(per_slot), active_slot(0), learn_queue_len(0), p(NULL), damaged(0)
  6. {
  7. this->slots = new AutomationSlot[slots];
  8. memset(this->slots, 0, sizeof(AutomationSlot)*slots);
  9. for(int i=0; i<slots; ++i) {
  10. auto &s = this->slots[i];
  11. sprintf(s.name, "Slot %d", i);
  12. s.midi_cc = -1;
  13. s.learning = -1;
  14. s.automations = new Automation[per_slot];
  15. memset(s.automations, 0, sizeof(Automation)*per_slot);
  16. for(int j=0; j<per_slot; ++j) {
  17. s.automations[j].map.control_points = new float[control_points];
  18. s.automations[j].map.npoints = control_points;
  19. s.automations[j].map.gain = 100.0;
  20. s.automations[j].map.offset = 0.0;
  21. }
  22. }
  23. }
  24. AutomationMgr::~AutomationMgr(void)
  25. {
  26. }
  27. void AutomationMgr::createBinding(int slot, const char *path, bool start_midi_learn)
  28. {
  29. assert(p);
  30. const Port *port = p->apropos(path);
  31. if(!port) {
  32. fprintf(stderr, "[Zyn:Error] port '%s' does not exist\n", path);
  33. return;
  34. }
  35. auto meta = port->meta();
  36. if(!(meta.find("min") && meta.find("max"))) {
  37. if(!strstr(port->name, ":T")) {
  38. fprintf(stderr, "No bounds for '%s' known\n", path);
  39. return;
  40. }
  41. }
  42. if(meta.find("internal") || meta.find("no learn")) {
  43. fprintf(stderr, "[Warning] port '%s' is unlearnable\n", path);
  44. return;
  45. }
  46. int ind = -1;
  47. for(int i=0; i<per_slot; ++i) {
  48. if(slots[slot].automations[i].used == false) {
  49. ind = i;
  50. break;
  51. }
  52. }
  53. if(ind == -1)
  54. return;
  55. slots[slot].used = true;
  56. auto &au = slots[slot].automations[ind];
  57. au.used = true;
  58. au.active = true;
  59. au.param_type = 'i';
  60. if(strstr(port->name, ":f"))
  61. au.param_type = 'f';
  62. else if(strstr(port->name, ":T"))
  63. au.param_type = 'T';
  64. if(au.param_type == 'T') {
  65. au.param_min = 0.0;
  66. au.param_max = 1.0;
  67. } else {
  68. au.param_min = atof(meta["min"]);
  69. au.param_max = atof(meta["max"]);
  70. }
  71. strncpy(au.param_path, path, sizeof(au.param_path));
  72. au.map.gain = 100.0;
  73. au.map.offset = 0;
  74. updateMapping(slot, ind);
  75. if(start_midi_learn && slots[slot].learning == -1 && slots[slot].midi_cc == -1)
  76. slots[slot].learning = ++learn_queue_len;
  77. damaged = true;
  78. };
  79. void AutomationMgr::updateMapping(int slot_id, int sub)
  80. {
  81. if(slot_id >= nslots || slot_id < 0 || sub >= per_slot || sub < 0)
  82. return;
  83. auto &au = slots[slot_id].automations[sub];
  84. float mn = au.param_min;
  85. float mx = au.param_max;
  86. float center = (mn+mx)*(0.5 + au.map.offset/100.0);
  87. float range = (mx-mn)*au.map.gain/100.0;
  88. au.map.upoints = 2;
  89. au.map.control_points[0] = 0;
  90. au.map.control_points[1] = center-range/2.0;
  91. au.map.control_points[2] = 1;
  92. au.map.control_points[3] = center+range/2.0;
  93. }
  94. void AutomationMgr::setSlot(int slot_id, float value)
  95. {
  96. if(slot_id >= nslots || slot_id < 0)
  97. return;
  98. for(int i=0; i<per_slot; ++i)
  99. setSlotSub(slot_id, i, value);
  100. slots[slot_id].current_state = value;
  101. }
  102. void AutomationMgr::setSlotSub(int slot_id, int par, float value)
  103. {
  104. if(slot_id >= nslots || slot_id < 0 || par >= per_slot || par < 0)
  105. return;
  106. auto &au = slots[slot_id].automations[par];
  107. if(au.used == false)
  108. return;
  109. const char *path = au.param_path;
  110. float mn = au.param_min;
  111. float mx = au.param_max;
  112. float a = au.map.control_points[1];
  113. float b = au.map.control_points[3];
  114. char type = au.param_type;
  115. char msg[256] = {0};
  116. if(type == 'i') {
  117. float v = value*(b-a) + a;
  118. if(v > mx)
  119. v = mx;
  120. else if(v < mn)
  121. v = mn;
  122. rtosc_message(msg, 256, path, "i", (int)roundf(v));
  123. } else if(type == 'f') {
  124. float v = value*(b-a) + a;
  125. if(v > mx)
  126. v = mx;
  127. else if(v < mn)
  128. v = mn;
  129. rtosc_message(msg, 256, path, "f", v);
  130. } else if(type == 'T' || type == 'F') {
  131. float v = value*(b-a) + a;
  132. if(v > 0.5)
  133. v = 1.0;
  134. else
  135. v = 0.0;
  136. rtosc_message(msg, 256, path, v == 1.0 ? "T" : "F");
  137. } else
  138. return;
  139. if(backend)
  140. backend(msg);
  141. }
  142. float AutomationMgr::getSlot(int slot_id)
  143. {
  144. if(slot_id >= nslots || slot_id < 0)
  145. return 0.0;
  146. return slots[slot_id].current_state;
  147. }
  148. void AutomationMgr::clearSlot(int slot_id)
  149. {
  150. if(slot_id >= nslots || slot_id < 0)
  151. return;
  152. auto &s = slots[slot_id];
  153. s.active = false;
  154. s.used = false;
  155. if(s.learning)
  156. learn_queue_len--;
  157. for(int i=0; i<nslots; ++i)
  158. if(slots[i].learning > s.learning)
  159. slots[i].learning--;
  160. s.learning = -1;
  161. s.midi_cc = -1;
  162. s.current_state = 0;
  163. memset(s.name, 0, sizeof(s.name));
  164. sprintf(s.name, "Slot %d", slot_id);
  165. for(int i=0; i<per_slot; ++i)
  166. clearSlotSub(slot_id, i);
  167. damaged = true;
  168. }
  169. void AutomationMgr::clearSlotSub(int slot_id, int sub)
  170. {
  171. if(slot_id >= nslots || slot_id < 0 || sub >= per_slot || sub < 0)
  172. return;
  173. auto &a = slots[slot_id].automations[sub];
  174. a.used = false;
  175. a.active = false;
  176. a.relative = false;
  177. a.param_base_value = false;
  178. memset(a.param_path, 0, sizeof(a.param_path));
  179. a.param_type = 0;
  180. a.param_min = 0;
  181. a.param_max = 0;
  182. a.param_step = 0;
  183. a.map.gain = 100;
  184. a.map.offset = 0;
  185. damaged = true;
  186. }
  187. void AutomationMgr::setSlotSubGain(int slot_id, int sub, float f)
  188. {
  189. if(slot_id >= nslots || slot_id < 0 || sub >= per_slot || sub < 0)
  190. return;
  191. auto &m = slots[slot_id].automations[sub].map;
  192. m.gain = f;
  193. }
  194. float AutomationMgr::getSlotSubGain(int slot_id, int sub)
  195. {
  196. if(slot_id >= nslots || slot_id < 0 || sub >= per_slot || sub < 0)
  197. return 0.0;
  198. auto &m = slots[slot_id].automations[sub].map;
  199. return m.gain;
  200. }
  201. void AutomationMgr::setSlotSubOffset(int slot_id, int sub, float f)
  202. {
  203. if(slot_id >= nslots || slot_id < 0 || sub >= per_slot || sub < 0)
  204. return;
  205. auto &m = slots[slot_id].automations[sub].map;
  206. m.offset = f;
  207. }
  208. float AutomationMgr::getSlotSubOffset(int slot_id, int sub)
  209. {
  210. if(slot_id >= nslots || slot_id < 0 || sub >= per_slot || sub < 0)
  211. return 0.0;
  212. auto &m = slots[slot_id].automations[sub].map;
  213. return m.offset;
  214. }
  215. void AutomationMgr::setName(int slot_id, const char *msg)
  216. {
  217. if(slot_id >= nslots || slot_id < 0)
  218. return;
  219. strncpy(slots[slot_id].name, msg, sizeof(slots[slot_id].name));
  220. damaged = 1;
  221. }
  222. const char *AutomationMgr::getName(int slot_id)
  223. {
  224. if(slot_id >= nslots || slot_id < 0)
  225. return "";
  226. return slots[slot_id].name;
  227. }
  228. bool AutomationMgr::handleMidi(int channel, int cc, int val)
  229. {
  230. int ccid = channel*128 + cc;
  231. bool bound_cc = false;
  232. for(int i=0; i<nslots; ++i) {
  233. if(slots[i].midi_cc == ccid) {
  234. bound_cc = true;
  235. setSlot(i, val/127.0);
  236. }
  237. }
  238. if(bound_cc)
  239. return 1;
  240. //No bound CC, now to see if there's something to learn
  241. for(int i=0; i<nslots; ++i) {
  242. if(slots[i].learning == 1) {
  243. slots[i].learning = -1;
  244. slots[i].midi_cc = ccid;
  245. for(int j=0; j<nslots; ++j)
  246. if(slots[j].learning > 1)
  247. slots[j].learning -= 1;
  248. learn_queue_len--;
  249. setSlot(i, val/127.0);
  250. damaged = 1;
  251. break;
  252. }
  253. }
  254. return 0;
  255. }
  256. void AutomationMgr::set_ports(const struct Ports &p_) {
  257. p = &p_;
  258. };
  259. //
  260. // AutomationSlot *slots;
  261. // struct AutomationMgrImpl *impl;
  262. //};
  263. void AutomationMgr::set_instance(void *v)
  264. {
  265. this->instance = v;
  266. }
  267. void AutomationMgr::simpleSlope(int slot_id, int par, float slope, float offset)
  268. {
  269. if(slot_id >= nslots || slot_id < 0 || par >= per_slot || par < 0)
  270. return;
  271. auto &map = slots[slot_id].automations[par].map;
  272. map.upoints = 2;
  273. map.control_points[0] = 0;
  274. map.control_points[1] = -(slope/2)+offset;
  275. map.control_points[2] = 1;
  276. map.control_points[3] = slope/2+offset;
  277. }
  278. int AutomationMgr::free_slot(void) const
  279. {
  280. for(int i=0; i<nslots; ++i)
  281. if(!slots[i].used)
  282. return i;
  283. return -1;
  284. }