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.

372 lines
13KB

  1. /*
  2. * Copyright (c) 2013 Mark McCurry
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice (including the next
  12. * paragraph) shall be included in all copies or substantial portions of the
  13. * Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  19. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  20. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  22. * DEALINGS IN THE SOFTWARE.
  23. */
  24. #ifndef RTOSC_PORT_SUGAR
  25. #define RTOSC_PORT_SUGAR
  26. //Hack to workaround old incomplete decltype implementations
  27. #ifdef __GNUC__
  28. #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 7)
  29. template<typename T>
  30. struct rtosc_hack_decltype_t
  31. {
  32. typedef T type;
  33. };
  34. #define decltype(expr) rtosc_hack_decltype_t<decltype(expr)>::type
  35. #endif
  36. #endif
  37. //General macro utilities
  38. #define STRINGIFY2(a) #a
  39. #define STRINGIFY(a) STRINGIFY2(a)
  40. //Helper for documenting varargs
  41. #define IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...) N
  42. #define LAST_IMP(...) IMPL(__VA_ARGS__,9,8,7,6,5,4,3,2,1,0,0,0,0)
  43. #define DOC_IMP9(a,b,c,d,e,f,g,h,i) a b c d e f g h rDoc(i)
  44. #define DOC_IMP8(a,b,c,d,e,f,g,h) a b c d e f g rDoc(h)
  45. #define DOC_IMP7(a,b,c,d,e,f,g) a b c d e f rDoc(g)
  46. #define DOC_IMP6(a,b,c,d,e,f) a b c d e rDoc(f)
  47. #define DOC_IMP5(a,b,c,d,e) a b c d rDoc(e)
  48. #define DOC_IMP4(a,b,c,d) a b c rDoc(d)
  49. #define DOC_IMP3(a,b,c) a b rDoc(c)
  50. #define DOC_IMP2(a,b) a rDoc(b)
  51. #define DOC_IMP1(a) rDoc(a)
  52. #define DOC_IMP0() YOU_MUST_DOCUMENT_YOUR_PORTS
  53. #define DOC_IMP(count, ...) DOC_IMP ##count(__VA_ARGS__)
  54. #define DOC_I(count, ...) DOC_IMP(count,__VA_ARGS__)
  55. #define DOC(...) DOC_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)
  56. //XXX Currently unused macro
  57. #define MAC_EACH_0(mac, x, ...) INSUFFICIENT_ARGUMENTS_PROVIDED_TO_MAC_EACH
  58. #define MAC_EACH_1(mac, x, ...) mac(x)
  59. #define MAC_EACH_2(mac, x, ...) mac(x) MAC_EACH_1(mac, __VA_ARGS__)
  60. #define MAC_EACH_3(mac, x, ...) mac(x) MAC_EACH_2(mac, __VA_ARGS__)
  61. #define MAC_EACH_4(mac, x, ...) mac(x) MAC_EACH_3(mac, __VA_ARGS__)
  62. #define MAC_EACH_5(mac, x, ...) mac(x) MAC_EACH_4(mac, __VA_ARGS__)
  63. #define MAC_EACH_6(mac, x, ...) mac(x) MAC_EACH_5(mac, __VA_ARGS__)
  64. #define MAC_EACH_7(mac, x, ...) mac(x) MAC_EACH_6(mac, __VA_ARGS__)
  65. #define MAC_EACH_8(mac, x, ...) mac(x) MAC_EACH_7(mac, __VA_ARGS__)
  66. #define MAC_EACH_9(mac, x, ...) mac(x) MAC_EACH_8(mac, __VA_ARGS__)
  67. #define MAC_EACH_IMP(mac, count, ...) MAC_EACH_ ##count(mac,__VA_ARGS__)
  68. #define MAC_EACH_I(mac, count, ...) MAC_EACH_IMP(mac, count, __VA_ARGS__)
  69. #define MAC_EACH(mac, ...) MAC_EACH_I(mac, LAST_IMP(__VA_ARGS__), __VA_ARGS__)
  70. #define OPTIONS_IMP9(a,b,c,d,e,f,g,h,i) \
  71. rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
  72. rOpt(7,h) rOpt(8,i)
  73. #define OPTIONS_IMP8(a,b,c,d,e,f,g,h) \
  74. rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
  75. rOpt(7,h)
  76. #define OPTIONS_IMP7(a,b,c,d,e,f,g) \
  77. rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g)
  78. #define OPTIONS_IMP6(a,b,c,d,e,f) \
  79. rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f)
  80. #define OPTIONS_IMP5(a,b,c,d,e) \
  81. rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e)
  82. #define OPTIONS_IMP4(a,b,c,d) \
  83. rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d)
  84. #define OPTIONS_IMP3(a,b,c) \
  85. rOpt(0,a) rOpt(1,b) rOpt(2,c)
  86. #define OPTIONS_IMP2(a,b) \
  87. rOpt(0,a) rOpt(1,b)
  88. #define OPTIONS_IMP1(a) \
  89. rOpt(0,a)
  90. #define OPTIONS_IMP0() YOU_MUST_PROVIDE_OPTIONS
  91. #define OPTIONS_IMP(count, ...) OPTIONS_IMP ##count(__VA_ARGS__)
  92. #define OPTIONS_I(count, ...) OPTIONS_IMP(count, __VA_ARGS__)
  93. #define OPTIONS(...) OPTIONS_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)
  94. //Additional Change Callback (after parameters have been changed)
  95. //This can be used to queue up interpolation or parameter regen
  96. #define rChangeCb
  97. //Normal parameters
  98. #define rParam(name, ...) \
  99. {STRINGIFY(name) "::c", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamCb(name)}
  100. #define rParamF(name, ...) \
  101. {STRINGIFY(name) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamFCb(name)}
  102. #define rParamI(name, ...) \
  103. {STRINGIFY(name) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamICb(name)}
  104. #define rToggle(name, ...) \
  105. {STRINGIFY(name) "::T:F",rProp(parameter) DOC(__VA_ARGS__), NULL, rToggleCb(name)}
  106. #define rOption(name, ...) \
  107. {STRINGIFY(name) "::i:c",rProp(parameter) DOC(__VA_ARGS__), NULL, rOptionCb(name)}
  108. //Array operators
  109. #define rArrayF(name, length, ...) \
  110. {STRINGIFY(name) "#" STRINGIFY(length) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayFCb(name)}
  111. #define rArray(name, length, ...) \
  112. {STRINGIFY(name) "#" STRINGIFY(length) "::c", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayCb(name)}
  113. #define rArrayT(name, length, ...) \
  114. {STRINGIFY(name) "#" STRINGIFY(length) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayTCb(name)}
  115. #define rArrayI(name, length, ...) \
  116. {STRINGIFY(name) "#" STRINGIFY(length) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayICb(name)}
  117. //Method callback Actions
  118. #define rAction(name, ...) \
  119. {STRINGIFY(name) ":", DOC(__VA_ARGS__), NULL, rActionCb(name)}
  120. #define rActioni(name, ...) \
  121. {STRINGIFY(name) ":i", DOC(__VA_ARGS__), NULL, rActioniCb(name)}
  122. //Alias operators
  123. #define rParams(name, length, ...) \
  124. rArray(name, length, __VA_ARGS__), \
  125. {STRINGIFY(name) ":", rProp(alias), NULL, rParamsCb(name, length)}
  126. template<class T> constexpr T spice(T*t) {return *t;}
  127. //Recursion [two ports in one for pointer manipulation]
  128. #define rRecur(name, ...) \
  129. {STRINGIFY(name) "/", DOC(__VA_ARGS__), &decltype(rObject::name)::ports, rRecurCb(name)}, \
  130. {STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)}
  131. #define rRecurp(name, ...) \
  132. {STRINGIFY(name) "/", DOC(__VA_ARGS__), \
  133. &decltype(spice(rObject::name))::ports, \
  134. rRecurpCb(name)}
  135. #define rRecurs(name, length, ...) \
  136. {STRINGIFY(name) "#" STRINGIFY(length)"/", DOC(__VA_ARGS__), \
  137. &decltype(spice(&rObject::name[0]))::ports, \
  138. rRecursCb(name, length)}
  139. //Technically this is a pointer pointer method...
  140. #define rRecursp(name, length, ...) \
  141. {STRINGIFY(name)"#" STRINGIFY(length) "/", DOC(__VA_ARGS__), \
  142. &decltype(spice(rObject::name[0]))::ports, \
  143. rRecurspCb(name)}
  144. //{STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)}
  145. //Misc
  146. #define rDummy(name, ...) {STRINIFY(name), rProp(dummy), NULL, [](msg_t, rtosc::RtData &){}}
  147. #define rString(name, len, ...) \
  148. {STRINGIFY(name) "::s", rMap(length, len) DOC(__VA_ARGS__), NULL, rStringCb(name,len)}
  149. //General property operators
  150. #define rMap(name, value) ":" STRINGIFY(name) "\0=" STRINGIFY(value) "\0"
  151. #define rProp(name) ":" STRINGIFY(name) "\0"
  152. //Scaling property
  153. #define rLinear(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, linear)
  154. #define rLog(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, logarithmic)
  155. //Special values
  156. #define rSpecial(doc) ":special\0" STRINGIFY(doc) "\0"
  157. #define rCentered ":centered\0"
  158. //Misc properties
  159. #define rDoc(doc) ":documentation\0=" doc "\0"
  160. #define rOpt(numeric,symbolic) rMap(map numeric, symbolic)
  161. #define rOptions(...) OPTIONS(__VA_ARGS__)
  162. //Callback Implementations
  163. #define rBOIL_BEGIN [](const char *msg, rtosc::RtData &data) { \
  164. (void) msg; (void) data; \
  165. rObject *obj = (rObject*) data.obj;(void) obj; \
  166. const char *args = rtosc_argument_string(msg); (void) args;\
  167. const char *loc = data.loc; (void) loc;\
  168. auto prop = data.port->meta(); (void) prop;
  169. #define rBOIL_END }
  170. #define rLIMIT(var, convert) \
  171. if(prop["min"] && var < (decltype(var))convert(prop["min"])) \
  172. var = convert(prop["min"]);\
  173. if(prop["max"] && var > (decltype(var))convert(prop["max"])) \
  174. var = convert(prop["max"]);
  175. #define rTYPE(n) decltype(obj->n)
  176. #define rAPPLY(n,t) if(obj->n != var) data.reply("undo_change", "s" #t #t, data.loc, obj->n, var); obj->n = var;
  177. #define rParamCb(name) rBOIL_BEGIN \
  178. if(!strcmp("", args)) {\
  179. data.reply(loc, "c", obj->name); \
  180. } else { \
  181. rTYPE(name) var = rtosc_argument(msg, 0).i; \
  182. rLIMIT(var, atoi) \
  183. rAPPLY(name, c) \
  184. data.broadcast(loc, "c", obj->name);\
  185. rChangeCb \
  186. } rBOIL_END
  187. #define rParamFCb(name) rBOIL_BEGIN \
  188. if(!strcmp("", args)) {\
  189. data.reply(loc, "f", obj->name); \
  190. } else { \
  191. rTYPE(name) var = rtosc_argument(msg, 0).f; \
  192. rLIMIT(var, atof) \
  193. rAPPLY(name, f) \
  194. data.broadcast(loc, "f", obj->name);\
  195. rChangeCb \
  196. } rBOIL_END
  197. #define rParamICb(name) rBOIL_BEGIN \
  198. if(!strcmp("", args)) {\
  199. data.reply(loc, "i", obj->name); \
  200. } else { \
  201. rTYPE(name) var = rtosc_argument(msg, 0).i; \
  202. rLIMIT(var, atoi) \
  203. rAPPLY(name, i) \
  204. data.broadcast(loc, "i", obj->name);\
  205. rChangeCb \
  206. } rBOIL_END
  207. //TODO finish me (include string mapper action?)
  208. #define rOptionCb(name) rBOIL_BEGIN \
  209. if(!strcmp("", args)) {\
  210. data.reply(loc, "i", obj->name); \
  211. } else { \
  212. rTYPE(name) var = rtosc_argument(msg, 0).i; \
  213. rLIMIT(var, atoi) \
  214. rAPPLY(name, i) \
  215. data.broadcast(loc, rtosc_argument_string(msg), obj->name);\
  216. rChangeCb \
  217. } rBOIL_END
  218. #define rToggleCb(name) rBOIL_BEGIN \
  219. if(!strcmp("", args)) {\
  220. data.reply(loc, obj->name ? "T" : "F"); \
  221. } else { \
  222. if(obj->name != rtosc_argument(msg, 0).T) { \
  223. data.broadcast(loc, args);\
  224. rChangeCb \
  225. } \
  226. obj->name = rtosc_argument(msg, 0).T; \
  227. } rBOIL_END
  228. #define SNIP \
  229. while(*msg && *msg!='/') ++msg; \
  230. msg = *msg ? msg+1 : msg;
  231. #define rRecurCb(name) rBOIL_BEGIN \
  232. data.obj = &obj->name; \
  233. SNIP \
  234. decltype(obj->name)::ports.dispatch(msg, data); \
  235. rBOIL_END
  236. #define rRecurPtrCb(name) rBOIL_BEGIN \
  237. void *ptr = &obj->name; \
  238. data.reply(loc, "b", sizeof(void*), &ptr); \
  239. rBOIL_END
  240. #define rRecurpCb(name) rBOIL_BEGIN \
  241. if(obj->name == NULL) return; \
  242. data.obj = obj->name; \
  243. SNIP \
  244. decltype(spice(rObject::name))::ports.dispatch(msg, data); \
  245. rBOIL_END
  246. #define rRecursCb(name, length) rBOILS_BEGIN \
  247. data.obj = &obj->name[idx]; \
  248. SNIP \
  249. decltype(spice(rObject::name))::ports.dispatch(msg, data); \
  250. rBOILS_END
  251. #define rRecurspCb(name) rBOILS_BEGIN \
  252. data.obj = obj->name[idx]; \
  253. SNIP \
  254. decltype(spice(rObject::name[0]))::ports.dispatch(msg, data); \
  255. rBOILS_END
  256. #define rActionCb(name) rBOIL_BEGIN obj->name(); rBOIL_END
  257. #define rActioniCb(name) rBOIL_BEGIN \
  258. obj->name(rtosc_argument(msg,0).i); rBOIL_END
  259. //Array ops
  260. #define rBOILS_BEGIN rBOIL_BEGIN \
  261. const char *mm = msg; \
  262. while(*mm && !isdigit(*mm)) ++mm; \
  263. unsigned idx = atoi(mm);
  264. #define rBOILS_END rBOIL_END
  265. #define rArrayCb(name) rBOILS_BEGIN \
  266. if(!strcmp("", args)) {\
  267. data.reply(loc, "c", obj->name[idx]); \
  268. } else { \
  269. char var = rtosc_argument(msg, 0).i; \
  270. rLIMIT(var, atoi) \
  271. rAPPLY(name[idx], c) \
  272. data.broadcast(loc, "c", obj->name[idx]);\
  273. rChangeCb \
  274. } rBOILS_END
  275. #define rArrayFCb(name) rBOILS_BEGIN \
  276. if(!strcmp("", args)) {\
  277. data.reply(loc, "f", obj->name[idx]); \
  278. } else { \
  279. float var = rtosc_argument(msg, 0).f; \
  280. rLIMIT(var, atof) \
  281. rAPPLY(name[idx], f) \
  282. data.broadcast(loc, "f", obj->name[idx]);\
  283. } rBOILS_END
  284. #define rArrayTCb(name) rBOILS_BEGIN \
  285. if(!strcmp("", args)) {\
  286. data.reply(loc, obj->name[idx] ? "T" : "F"); \
  287. } else { \
  288. if(obj->name[idx] != rtosc_argument(msg, 0).T) { \
  289. data.broadcast(loc, args);\
  290. rChangeCb \
  291. } \
  292. obj->name[idx] = rtosc_argument(msg, 0).T; \
  293. } rBOILS_END
  294. #define rArrayICb(name) rBOILS_BEGIN \
  295. if(!strcmp("", args)) {\
  296. data.reply(loc, "i", obj->name[idx]); \
  297. } else { \
  298. char var = rtosc_argument(msg, 0).i; \
  299. rLIMIT(var, atoi) \
  300. rAPPLY(name[idx], i) \
  301. data.broadcast(loc, "i", obj->name[idx]);\
  302. rChangeCb \
  303. } rBOILS_END
  304. #define rParamsCb(name, length) rBOIL_BEGIN \
  305. data.reply(loc, "b", length, obj->name); rBOIL_END
  306. #define rStringCb(name, length) rBOIL_BEGIN \
  307. if(!strcmp("", args)) {\
  308. data.reply(loc, "s", obj->name); \
  309. } else { \
  310. strncpy(obj->name, rtosc_argument(msg, 0).s, length); \
  311. data.broadcast(loc, "s", obj->name);\
  312. rChangeCb \
  313. } rBOIL_END
  314. #endif