Real Time Open Sound Control librtosc
port-sugar.h
1/*
2 * Copyright (c) 2016 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
25#include <assert.h>
26#include <type_traits>
27#include <cstring>
28
29#ifndef RTOSC_PORT_SUGAR
30#define RTOSC_PORT_SUGAR
31
32//Hack to workaround old incomplete decltype implementations
33#ifdef __GNUC__
34#ifndef __clang__
35#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 7)
36template<typename T>
37struct rtosc_hack_decltype_t
38{
39 typedef T type;
40};
41
42#define decltype(expr) rtosc_hack_decltype_t<decltype(expr)>::type
43#endif
44#endif
45#endif
46
47//General macro utilities
48#define STRINGIFY2(a) #a
49#define STRINGIFY(a) STRINGIFY2(a)
50
51//Helper for documenting varargs
52#define IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N, ...) N
53#define LAST_IMP(...) IMPL(__VA_ARGS__,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
54#define DOC_IMP24(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x) \
55 a b c d e f g h i j k l m n o p q r s t u v w rDoc(x)
56#define DOC_IMP23(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w) \
57 a b c d e f g h i j k l m n o p q r s t u v rDoc(w)
58#define DOC_IMP22(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v) \
59 a b c d e f g h i j k l m n o p q r s t u rDoc(v)
60#define DOC_IMP21(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u) \
61 a b c d e f g h i j k l m n o p q r s t rDoc(u)
62#define DOC_IMP20(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t) \
63 a b c d e f g h i j k l m n o p q r s rDoc(t)
64#define DOC_IMP19(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s) \
65 a b c d e f g h i j k l m n o p q r rDoc(s)
66#define DOC_IMP18(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r) \
67 a b c d e f g h i j k l m n o p q rDoc(r)
68#define DOC_IMP17(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) \
69 a b c d e f g h i j k l m n o p rDoc(q)
70#define DOC_IMP16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \
71 a b c d e f g h i j k l m n o rDoc(p)
72#define DOC_IMP15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \
73 a b c d e f g h i j k l m n rDoc(o)
74#define DOC_IMP14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \
75 a b c d e f g h i j k l m rDoc(n)
76#define DOC_IMP13(a,b,c,d,e,f,g,h,i,j,k,l,m) \
77 a b c d e f g h i j k l rDoc(m)
78#define DOC_IMP12(a,b,c,d,e,f,g,h,i,j,k,l) a b c d e f g h i j k rDoc(l)
79#define DOC_IMP11(a,b,c,d,e,f,g,h,i,j,k) a b c d e f g h i j rDoc(k)
80#define DOC_IMP10(a,b,c,d,e,f,g,h,i,j) a b c d e f g h i rDoc(j)
81#define DOC_IMP9(a,b,c,d,e,f,g,h,i) a b c d e f g h rDoc(i)
82#define DOC_IMP8(a,b,c,d,e,f,g,h) a b c d e f g rDoc(h)
83#define DOC_IMP7(a,b,c,d,e,f,g) a b c d e f rDoc(g)
84#define DOC_IMP6(a,b,c,d,e,f) a b c d e rDoc(f)
85#define DOC_IMP5(a,b,c,d,e) a b c d rDoc(e)
86#define DOC_IMP4(a,b,c,d) a b c rDoc(d)
87#define DOC_IMP3(a,b,c) a b rDoc(c)
88#define DOC_IMP2(a,b) a rDoc(b)
89#define DOC_IMP1(a) rDoc(a)
90#define DOC_IMP0() YOU_MUST_DOCUMENT_YOUR_PORTS
91#define DOC_IMP(count, ...) DOC_IMP ##count(__VA_ARGS__)
92#define DOC_I(count, ...) DOC_IMP(count,__VA_ARGS__)
93#define DOC(...) DOC_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)
94
95
96#define rINC(x) rINC_ ## x
97#define rINC_0 1
98#define rINC_1 2
99#define rINC_2 3
100#define rINC_3 4
101#define rINC_4 5
102#define rINC_5 6
103#define rINC_6 7
104#define rINC_7 8
105#define rINC_8 9
106#define rINC_9 10
107#define rINC_10 11
108#define rINC_11 12
109#define rINC_12 13
110#define rINC_13 14
111#define rINC_14 15
112#define rINC_15 16
113#define rINC_16 17
114
115//Helper for applying macro on varargs
116//arguments: counting offset, macro, macro args
117#define MAC_EACH_0(o,m,d,x, ...) INSUFFICIENT_ARGUMENTS_PROVIDED_TO_MAC_EACH
118#define MAC_EACH_1(o,m,d,x, ...) m(o,d,x)
119#define MAC_EACH_2(o,m,d,x, ...) m(o,d,x) MAC_EACH_1(rINC(o),m,d, __VA_ARGS__)
120#define MAC_EACH_3(o,m,d,x, ...) m(o,d,x) MAC_EACH_2(rINC(o),m,d, __VA_ARGS__)
121#define MAC_EACH_4(o,m,d,x, ...) m(o,d,x) MAC_EACH_3(rINC(o),m,d, __VA_ARGS__)
122#define MAC_EACH_5(o,m,d,x, ...) m(o,d,x) MAC_EACH_4(rINC(o),m,d, __VA_ARGS__)
123#define MAC_EACH_6(o,m,d,x, ...) m(o,d,x) MAC_EACH_5(rINC(o),m,d, __VA_ARGS__)
124#define MAC_EACH_7(o,m,d,x, ...) m(o,d,x) MAC_EACH_6(rINC(o),m,d, __VA_ARGS__)
125#define MAC_EACH_8(o,m,d,x, ...) m(o,d,x) MAC_EACH_7(rINC(o),m,d, __VA_ARGS__)
126#define MAC_EACH_9(o,m,d,x, ...) m(o,d,x) MAC_EACH_8(rINC(o),m,d, __VA_ARGS__)
127#define MAC_EACH_10(o,m,d,x, ...) m(o,d,x) MAC_EACH_9(rINC(o),m,d, __VA_ARGS__)
128#define MAC_EACH_11(o,m,d,x, ...) m(o,d,x) MAC_EACH_10(rINC(o),m,d, __VA_ARGS__)
129#define MAC_EACH_12(o,m,d,x, ...) m(o,d,x) MAC_EACH_11(rINC(o),m,d, __VA_ARGS__)
130#define MAC_EACH_13(o,m,d,x, ...) m(o,d,x) MAC_EACH_12(rINC(o),m,d, __VA_ARGS__)
131#define MAC_EACH_14(o,m,d,x, ...) m(o,d,x) MAC_EACH_13(rINC(o),m,d, __VA_ARGS__)
132#define MAC_EACH_15(o,m,d,x, ...) m(o,d,x) MAC_EACH_14(rINC(o),m,d, __VA_ARGS__)
133#define MAC_EACH_16(o,m,d,x, ...) m(o,d,x) MAC_EACH_15(rINC(o),m,d, __VA_ARGS__)
134
135#define MAC_EACH_IMP(off, mac, data, count, ...) \
136 MAC_EACH_ ##count(off, mac, data, __VA_ARGS__)
137#define MAC_EACH_I(off, mac, data, count, ...) \
138 MAC_EACH_IMP(off, mac, data, count, __VA_ARGS__)
139#define MAC_EACH_OFF(off, mac, data, ...) \
140 MAC_EACH_I(off, mac, data, LAST_IMP(__VA_ARGS__), __VA_ARGS__)
141#define MAC_EACH(mac, data, ...) MAC_EACH_OFF(0, mac, data, __VA_ARGS__)
142
143// 1 2 3 4 5 6 7 8 9101112131415161718192021222324
144#define OPTIONS_IMP24(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x) \
145 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
146 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
147 rOpt(14,o)rOpt(15,p)rOpt(16,q)rOpt(17,r)rOpt(18,s)rOpt(19,t)rOpt(20,u)\
148 rOpt(21,v)rOpt(22,w)rOpt(23,x)
149// 1 2 3 4 5 6 7 8 91011121314151617181920212223
150#define OPTIONS_IMP23(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w) \
151 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
152 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
153 rOpt(14,o)rOpt(15,p)rOpt(16,q)rOpt(17,r)rOpt(18,s)rOpt(19,t)rOpt(20,u)\
154 rOpt(21,v)rOpt(22,w)
155// 1 2 3 4 5 6 7 8 910111213141516171819202122
156#define OPTIONS_IMP22(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v) \
157 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
158 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
159 rOpt(14,o)rOpt(15,p)rOpt(16,q)rOpt(17,r)rOpt(18,s)rOpt(19,t)rOpt(20,u)\
160 rOpt(21,v)
161// 1 2 3 4 5 6 7 8 9101112131415161718192021
162#define OPTIONS_IMP21(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u) \
163 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
164 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
165 rOpt(14,o)rOpt(15,p)rOpt(16,q)rOpt(17,r)rOpt(18,s)rOpt(19,t)rOpt(20,u)
166// 1 2 3 4 5 6 7 8 91011121314151617181920
167#define OPTIONS_IMP20(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t) \
168 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
169 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
170 rOpt(14,o)rOpt(15,p)rOpt(16,q)rOpt(17,r)rOpt(18,s)rOpt(19,t)
171// 1 2 3 4 5 6 7 8 910111213141516171819
172#define OPTIONS_IMP19(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s) \
173 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
174 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
175 rOpt(14,o)rOpt(15,p)rOpt(16,q)rOpt(17,r)rOpt(18,s)
176// 1 2 3 4 5 6 7 8 9101112131415161718
177#define OPTIONS_IMP18(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r) \
178 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
179 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
180 rOpt(14,o)rOpt(15,p)rOpt(16,q)rOpt(17,r)
181#define OPTIONS_IMP17(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) \
182 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
183 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
184 rOpt(14,o)rOpt(15,p)rOpt(16,q)
185#define OPTIONS_IMP16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \
186 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
187 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
188 rOpt(14,o)rOpt(15,p)
189#define OPTIONS_IMP15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \
190 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
191 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
192 rOpt(14,o)
193#define OPTIONS_IMP14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \
194 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
195 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)
196#define OPTIONS_IMP13(a,b,c,d,e,f,g,h,i,j,k,l,m) \
197 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
198 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)
199#define OPTIONS_IMP12(a,b,c,d,e,f,g,h,i,j,k,l) \
200 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
201 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)
202#define OPTIONS_IMP11(a,b,c,d,e,f,g,h,i,j,k) \
203 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
204 rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)
205#define OPTIONS_IMP10(a,b,c,d,e,f,g,h,i,j) \
206 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
207 rOpt(7,h) rOpt(8,i) rOpt(9,j)
208#define OPTIONS_IMP9(a,b,c,d,e,f,g,h,i) \
209 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
210 rOpt(7,h) rOpt(8,i)
211#define OPTIONS_IMP8(a,b,c,d,e,f,g,h) \
212 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
213 rOpt(7,h)
214#define OPTIONS_IMP7(a,b,c,d,e,f,g) \
215 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g)
216#define OPTIONS_IMP6(a,b,c,d,e,f) \
217 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f)
218#define OPTIONS_IMP5(a,b,c,d,e) \
219 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e)
220#define OPTIONS_IMP4(a,b,c,d) \
221 rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d)
222#define OPTIONS_IMP3(a,b,c) \
223 rOpt(0,a) rOpt(1,b) rOpt(2,c)
224#define OPTIONS_IMP2(a,b) \
225 rOpt(0,a) rOpt(1,b)
226#define OPTIONS_IMP1(a) \
227 rOpt(0,a)
228#define OPTIONS_IMP0() YOU_MUST_PROVIDE_OPTIONS
229#define OPTIONS_IMP(count, ...) OPTIONS_IMP ##count(__VA_ARGS__)
230#define OPTIONS_I(count, ...) OPTIONS_IMP(count, __VA_ARGS__)
231#define OPTIONS(...) OPTIONS_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)
232
233//Additional Change Callback (after parameters have been changed)
234//This can be used to queue up interpolation or parameter regen
235#define rChangeCb
236
237#define rCrossBroadcast(loc, cross) \
238 char part_loc[128]; \
239 strncpy(part_loc, loc, sizeof(part_loc)); \
240 part_loc[sizeof(part_loc) - 1] = '\0'; \
241 char *end = strrchr(part_loc, '/'); \
242 if(end) { \
243 strcpy(&end[1], STRINGIFY(cross)); \
244 data.broadcast(part_loc, "f", obj->cross); \
245 }
246
247//Normal parameters
248#define rParam(name, ...) \
249 {STRINGIFY(name) "::c", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamCb(name)}
250#define rParamF(name, ...) \
251 {STRINGIFY(name) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamFCb(name)}
252#define rParamI(name, ...) \
253 {STRINGIFY(name) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamICb(name)}
254#define rToggle(name, ...) \
255 {STRINGIFY(name) "::T:F",rProp(parameter) DOC(__VA_ARGS__), NULL, rToggleCb(name)}
256#define rOption(name, ...) \
257 {STRINGIFY(name) "::i:c:S",rProp(parameter) rProp(enumerated) DOC(__VA_ARGS__), NULL, rOptionCb(name)}
258
259//Array operators
260#define rArrayF(name, length, ...) \
261{STRINGIFY(name) "#" STRINGIFY(length) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayFCb(name)}
262#define rArrayT(name, length, ...) \
263{STRINGIFY(name) "#" STRINGIFY(length) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayTCb(name)}
264#define rArrayI(name, length, ...) \
265{STRINGIFY(name) "#" STRINGIFY(length) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayICb(name)}
266#define rArrayOption(name, length, ...) \
267{STRINGIFY(name) "#" STRINGIFY(length) "::i:c:S", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayOptionCb(name)}
268#define rArray rArrayI
269
270//Method callback Actions
271#define rAction(name, ...) \
272{STRINGIFY(name) ":", DOC(__VA_ARGS__), NULL, rActionCb(name)}
273#define rActioni(name, ...) \
274{STRINGIFY(name) ":i", DOC(__VA_ARGS__), NULL, rActioniCb(name)}
275
276
277//Alias operators
278#define rParams(name, length, ...) \
279rArray(name, length, __VA_ARGS__), \
280{STRINGIFY(name) ":", rProp(alias) rDoc("get all data from aliased array"), NULL, rParamsCb(name, length)}
281
282
283template<class T> constexpr T spice(T*t) {return *t;}
284
285//Recursion [two ports in one for pointer manipulation]
286#define rRecur(name, ...) \
287 {STRINGIFY(name) "/", DOC(__VA_ARGS__), &decltype(rObject::name)::ports, rRecurCb(name)}, \
288 {STRINGIFY(name) ":", rProp(internal) rDoc("get obj pointer"), NULL, rRecurPtrCb(name)}
289
290#define rRecurp(name, ...) \
291 {STRINGIFY(name) "/", DOC(__VA_ARGS__), \
292 &decltype(spice(rObject::name))::ports, \
293 rRecurpCb(name)}
294
295#define rRecurs(name, length, ...) \
296 {STRINGIFY(name) "#" STRINGIFY(length)"/", DOC(__VA_ARGS__), \
297 &decltype(spice(&rObject::name[0]))::ports, \
298 rRecursCb(name, length)}
299
300//Technically this is a pointer pointer method...
301#define rRecursp(name, length, ...) \
302 {STRINGIFY(name)"#" STRINGIFY(length) "/", DOC(__VA_ARGS__), \
303 &decltype(spice(rObject::name[0]))::ports, \
304 rRecurspCb(name)}
305
306//{STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)}
307
308//let this recurring parameter depend on another port
309//the path of the other port must be at the current level or one level above
310#define rEnabledBy(portname) rMap(enabled by, portname)
311#define rEnabledByCondition(cond_name) rEnabledBy(cond_name)
312#define rEnabledCondition(cond_name, condition) \
313 {STRINGIFY(cond_name) ":", rProp(internal), NULL, rEnabledIfCb(condition)}
314#define rEnabledIfCb(condition) rBOIL_BEGIN \
315 assert(!rtosc_narguments(msg)); \
316 data.reply(loc, (condition)?"T":"F"); \
317 rBOIL_END \
318
319#define rSelf(type, ...) \
320{"self:", rProp(internal) rMap(class, type) __VA_ARGS__ rDoc("port metadata"), 0, \
321 [](const char *, rtosc::RtData &d){ \
322 d.reply(d.loc, "b", sizeof(d.obj), &d.obj);}}\
323
324//Misc
325#define rDummy(name, ...) {STRINGIFY(name), rProp(dummy), NULL, [](msg_t, rtosc::RtData &){}}
326#define rString(name, len, ...) \
327 {STRINGIFY(name) "::s", rMap(length, len) rProp(parameter) DOC(__VA_ARGS__), NULL, rStringCb(name,len)}
328
329//General property operators
330#define rMap(name, value) ":" STRINGIFY(name) "\0=" STRINGIFY(value) "\0"
331#define rProp(name) ":" STRINGIFY(name) "\0"
332
333//Scaling property
334//This property describes the variable's input scale, which is in most cases
335//(not always) equal to the perception. Thus, if 0 is in the input scale, and
336//has no special meaning, rLinear shall be used.
337#define rLinear(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, linear)
338#define rLog(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, logarithmic)
339
340//Special values
341#define rSpecial(doc) ":special\0" STRINGIFY(doc) "\0"
342#define rCentered ":centered\0"
343
344//Default values
345#define rDefault(default_value_) rMap(default, default_value_)
346#define rDefaultId(default_value_) ":default\0=\"" STRINGIFY(default_value_) "\"S\0"
347//#define rDefaultArr(default_value_, idx_) rMap(default[idx_], default_value_)
348#define rPreset(no, default_value) \
349 ":default " STRINGIFY(no) "\0=" STRINGIFY(default_value) "\0"
350#define _rPreset(no, data, default_value) rPreset(no, default_value)
351#define rPresetsAt(offs, ...) MAC_EACH_OFF(offs, _rPreset, offs, __VA_ARGS__)
352#define _rPreset2(offs, data, preset_no) rPreset(preset_no, data)
353#define rPresetAtMulti(val, ...) MAC_EACH_OFF(0, _rPreset2, val, __VA_ARGS__)
354#define rPresets(...) rPresetsAt(0, __VA_ARGS__)
355#define rDefaultDepends(dep_path_) rMap(default depends, dep_path_)
356#define rDefaultMissing "" // macro to denote yet missing default values
358#define rNoDefaults ":no defaults\0"
359
360//Misc properties
361#define rDoc(doc) ":documentation\0=" doc "\0"
362#define rOpt(numeric,symbolic) rMap(map numeric, symbolic)
363#define rOptions(...) OPTIONS(__VA_ARGS__)
364#define rOptionsBound(...) rOptions(__VA_ARGS__), rLinear(0,LAST_IMP(__VA_ARGS__))
365
366//Zest Metadata
367#define rShort(name) ":shortname\0=" name "\0"
368
369
370//Callback Implementations
371#define rBOIL_BEGIN [](const char *msg, rtosc::RtData &data) { \
372 (void) msg; (void) data; \
373 rObject *obj = (rObject*) data.obj;(void) obj; \
374 const char *args = rtosc_argument_string(msg); (void) args;\
375 const char *loc = data.loc; (void) loc;\
376 auto prop = data.port->meta(); (void) prop;
377
378#define rBOIL_END }
379
380#define rLIMIT(var, convert) \
381 if(prop["min"] && var < (decltype(var)) convert(prop["min"])) \
382 var = (decltype(var)) convert(prop["min"]);\
383 if(prop["max"] && var > (decltype(var)) convert(prop["max"])) \
384 var = (decltype(var)) convert(prop["max"]);
385
386#define rTYPE(n) decltype(obj->n)
387
388#define rCAPPLY(getcode, t, setcode) if((decltype(var))(getcode) != var) data.reply("/undo_change", "s" #t #t, data.loc, static_cast<int>(getcode), var); setcode;
389#define rAPPLY(n,t) rCAPPLY(obj->n, t, obj->n = var)
390
391#define rParamCb(name) rBOIL_BEGIN \
392 if(!strcmp("", args)) {\
393 data.reply(loc, "c", obj->name); \
394 } else { \
395 rTYPE(name) var = rtosc_argument(msg, 0).i; \
396 rLIMIT(var, atoi) \
397 rAPPLY(name, c) \
398 data.broadcast(loc, "c", obj->name);\
399 rChangeCb; \
400 } rBOIL_END
401
402#define rParamFCb(name) rBOIL_BEGIN \
403 if(!strcmp("", args)) {\
404 data.reply(loc, "f", obj->name); \
405 } else { \
406 rTYPE(name) var = rtosc_argument(msg, 0).f; \
407 rLIMIT(var, atof) \
408 rAPPLY(name, f) \
409 data.broadcast(loc, "f", obj->name);\
410 rChangeCb; \
411 } rBOIL_END
412
413#define rParamICb(name) rBOIL_BEGIN \
414 if(!strcmp("", args)) {\
415 data.reply(loc, "i", obj->name); \
416 } else { \
417 rTYPE(name) var = rtosc_argument(msg, 0).i; \
418 rLIMIT(var, atoi) \
419 rAPPLY(name, i) \
420 data.broadcast(loc, "i", obj->name);\
421 rChangeCb; \
422 } rBOIL_END
423
424#define rCOptionCb_(getcode, setcode) { \
425 if(!strcmp("", args)) {\
426 data.reply(loc, "i", static_cast<int>(getcode)); \
427 } else if(!strcmp("s", args) || !strcmp("S", args)) { \
428 int var = \
429 enum_key(prop, rtosc_argument(msg, 0).s); \
430 /* make sure we have no out-of-bound options */ \
431 assert(!prop["min"] || \
432 var >= atoi(prop["min"])); \
433 assert(!prop["max"] || \
434 var <= atoi(prop["max"])); \
435 rCAPPLY(getcode, i, setcode) \
436 data.broadcast(loc, "i", getcode); \
437 rChangeCb; \
438 } else {\
439 int var = \
440 rtosc_argument(msg, 0).i; \
441 rLIMIT(var, atoi) \
442 rCAPPLY(getcode, i, setcode) \
443 data.broadcast(loc, rtosc_argument_string(msg), getcode);\
444 rChangeCb; \
445 } \
446 }
447
448#define rOptionCb_(name) rCOptionCb_(obj->name, obj->name = static_cast<std::remove_reference<decltype(obj->name)>::type>(var))
449
450#define rOptionCb(name) rBOIL_BEGIN \
451 rOptionCb_(name) \
452 rBOIL_END
453
454#define rCOptionCb(getcode, setcode) rBOIL_BEGIN \
455 rCOptionCb_(getcode, setcode) \
456 rBOIL_END
457
458
459#define rToggleCb(name) rBOIL_BEGIN \
460 if(!strcmp("", args)) {\
461 data.reply(loc, obj->name ? "T" : "F"); \
462 } else { \
463 if(obj->name != rtosc_argument(msg, 0).T) { \
464 data.broadcast(loc, args);\
465 obj->name = rtosc_argument(msg, 0).T; \
466 rChangeCb; \
467 } \
468 } rBOIL_END
469
470#define SNIP \
471 while(*msg && *msg!='/') ++msg; \
472 msg = *msg ? msg+1 : msg;
473
474#define rRecurCb(name) rBOIL_BEGIN \
475 data.obj = &obj->name; \
476 SNIP \
477 decltype(obj->name)::ports.dispatch(msg, data); \
478 rBOIL_END
479
480#define rRecurPtrCb(name) rBOIL_BEGIN \
481 void *ptr = &obj->name; \
482 data.reply(loc, "b", sizeof(void*), &ptr); \
483 rBOIL_END
484
485#define rRecurpCb(name) rBOIL_BEGIN \
486 data.obj = obj->name; \
487 if(obj->name == NULL) return; \
488 SNIP \
489 decltype(spice(rObject::name))::ports.dispatch(msg, data); \
490 rBOIL_END
491
492#define rRecursCb(name, length) rBOILS_BEGIN \
493 data.obj = &obj->name[idx]; \
494 SNIP \
495 decltype(spice(rObject::name))::ports.dispatch(msg, data); \
496 rBOILS_END
497
498#define rRecurspCb(name) rBOILS_BEGIN \
499 data.obj = obj->name[idx]; \
500 SNIP \
501 decltype(spice(rObject::name[0]))::ports.dispatch(msg, data); \
502 rBOILS_END
503
504#define rActionCb(name) rBOIL_BEGIN obj->name(); rBOIL_END
505#define rActioniCb(name) rBOIL_BEGIN \
506 obj->name(rtosc_argument(msg,0).i); rBOIL_END
507
508//Array ops
509
510#define rBOILS_BEGIN rBOIL_BEGIN \
511 const char *mm = msg; \
512 while(*mm && !isdigit(*mm)) ++mm; \
513 unsigned idx = atoi(mm);
514
515#define rBOILS_END rBOIL_END
516
517
518#define rArrayFCb(name) rBOILS_BEGIN \
519 if(!strcmp("", args)) {\
520 data.reply(loc, "f", obj->name[idx]); \
521 } else { \
522 float var = rtosc_argument(msg, 0).f; \
523 rLIMIT(var, atof) \
524 rAPPLY(name[idx], f) \
525 data.broadcast(loc, "f", obj->name[idx]);\
526 } rBOILS_END
527
528#define rArrayTCb(name) rBOILS_BEGIN \
529 if(!strcmp("", args)) {\
530 data.reply(loc, obj->name[idx] ? "T" : "F"); \
531 } else { \
532 if(obj->name[idx] != rtosc_argument(msg, 0).T) { \
533 data.broadcast(loc, args);\
534 rChangeCb; \
535 } \
536 obj->name[idx] = rtosc_argument(msg, 0).T; \
537 } rBOILS_END
538
539#define rArrayTCbMember(name, member) rBOILS_BEGIN \
540 if(!strcmp("", args)) {\
541 data.reply(loc, obj->name[idx].member ? "T" : "F"); \
542 } else { \
543 if(obj->name[idx].member != rtosc_argument(msg, 0).T) { \
544 data.broadcast(loc, args);\
545 rChangeCb; \
546 } \
547 obj->name[idx].member = rtosc_argument(msg, 0).T; \
548 } rBOILS_END
549
550
551#define rArrayICb(name) rBOILS_BEGIN \
552 if(!strcmp("", args)) {\
553 data.reply(loc, "i", obj->name[idx]); \
554 } else { \
555 char var = rtosc_argument(msg, 0).i; \
556 rLIMIT(var, atoi) \
557 rAPPLY(name[idx], i) \
558 data.broadcast(loc, "i", obj->name[idx]);\
559 rChangeCb; \
560 } rBOILS_END
561
562
563#define rArrayOptionCb(name) rBOILS_BEGIN \
564 rOptionCb_(name[idx]) \
565 rBOILS_END
566
567#define rParamsCb(name, length) rBOIL_BEGIN \
568 data.reply(loc, "b", length, obj->name); rBOIL_END
569
570#define rStringCb(name, length) rBOIL_BEGIN \
571 if(!strcmp("", args)) {\
572 data.reply(loc, "s", obj->name); \
573 } else { \
574 strncpy(obj->name, rtosc_argument(msg, 0).s, length-1); \
575 obj->name[length-1] = '\0'; \
576 data.broadcast(loc, "s", obj->name);\
577 rChangeCb; \
578 } rBOIL_END
579
580
581#endif