├── LICENSE
├── LightpadCello.littlefoot
└── README.md
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Brian Wang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LightpadCello.littlefoot:
--------------------------------------------------------------------------------
1 | /*
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | */
29 |
30 | //
31 | // LightpadCello.littlefoot
32 | // LightpadCello
33 | //
34 | // Created by Brian Ping-Yao Wang on 1/11/18.
35 | // Copyright © 2018 Brian Ping-Yao Wang. All rights reserved.
36 | //
37 |
38 | // GLOBAL DATA STRUCTURES (stored in heap)
39 | // NOTE: The heap size is 512 bytes.
40 |
41 | // touch positions received from all blocks (used only on main block)
42 | // index: (block_index, touch_base_1_index)
43 | // fields: isDown, x, y, z per
44 | int g_blockTouchState_heapAddr;
45 | int g_blockTouchState_numBlocks;
46 | int g_blockTouchState_numTouches;
47 | int g_blockTouchState_heapSizePerUnit;
48 | int g_blockTouchState_heapAddrOffset_isDown;
49 | int g_blockTouchState_heapAddrOffset_x;
50 | int g_blockTouchState_heapAddrOffset_y;
51 | int g_blockTouchState_heapAddrOffset_z;
52 |
53 | int blockTouchStateAddr(int blockIndex, int touchIndex, int offset) {
54 | int addrBase = g_blockTouchState_heapAddr;
55 | addrBase += blockIndex * g_blockTouchState_numTouches * g_blockTouchState_heapSizePerUnit;
56 | addrBase += (touchIndex-1) * g_blockTouchState_heapSizePerUnit;
57 | return addrBase + offset;
58 | }
59 |
60 | int getBlockTouchIsDown(int blockIndex, int touchIndex) {
61 | return getHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_isDown));
62 | }
63 |
64 | int getBlockTouchX(int blockIndex, int touchIndex) {
65 | return getHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_x));
66 | }
67 |
68 | int getBlockTouchY(int blockIndex, int touchIndex) {
69 | return getHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_y));
70 | }
71 |
72 | int getBlockTouchZ(int blockIndex, int touchIndex) {
73 | return getHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_z));
74 | }
75 |
76 | void setBlockTouchIsDown(int blockIndex, int touchIndex, int value) {
77 | setHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_isDown), value);
78 | }
79 |
80 | void setBlockTouchX(int blockIndex, int touchIndex, int value) {
81 | setHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_x), value);
82 | }
83 |
84 | void setBlockTouchY(int blockIndex, int touchIndex, int value) {
85 | setHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_y), value);
86 | }
87 |
88 | void setBlockTouchZ(int blockIndex, int touchIndex, int value) {
89 | setHeapInt(blockTouchStateAddr(blockIndex, touchIndex, g_blockTouchState_heapAddrOffset_z), value);
90 | }
91 |
92 | // for drawing strings on each block
93 | // index: string
94 | // fields: isDown, drawPos, isPlaying
95 | int g_stringDrawState_heapAddr;
96 | int g_stringDrawState_numStrings;
97 | int g_stringDrawState_heapSizePerUnit;
98 | int g_stringDrawState_heapAddrOffset_isDown;
99 | int g_stringDrawState_heapAddrOffset_drawPos;
100 | int g_stringDrawState_heapAddrOffset_isPlaying;
101 |
102 |
103 | int stringDrawStateAddr(int stringIndex, int offset) {
104 | int addrBase = g_stringDrawState_heapAddr;
105 | addrBase += stringIndex * g_stringDrawState_heapSizePerUnit;
106 | return addrBase + offset;
107 | }
108 |
109 | int getStringIsDown(int stringIndex) {
110 | return getHeapInt(stringDrawStateAddr(stringIndex, g_stringDrawState_heapAddrOffset_isDown));
111 | }
112 |
113 | int getStringDrawPos(int stringIndex) {
114 | return getHeapInt(stringDrawStateAddr(stringIndex, g_stringDrawState_heapAddrOffset_drawPos));
115 | }
116 |
117 | int getStringIsPlaying(int stringIndex) {
118 | return getHeapInt(stringDrawStateAddr(stringIndex, g_stringDrawState_heapAddrOffset_isPlaying));
119 | }
120 |
121 | void setStringIsDown(int stringIndex, int value) {
122 | setHeapInt(stringDrawStateAddr(stringIndex, g_stringDrawState_heapAddrOffset_isDown), value);
123 | }
124 |
125 | void setStringDrawPos(int stringIndex, int value) {
126 | setHeapInt(stringDrawStateAddr(stringIndex, g_stringDrawState_heapAddrOffset_drawPos), value);
127 | }
128 |
129 | void setStringIsPlaying(int stringIndex, int value) {
130 | setHeapInt(stringDrawStateAddr(stringIndex, g_stringDrawState_heapAddrOffset_isPlaying), value);
131 | }
132 |
133 |
134 | // effective touch position/id on each string (used only on main block)
135 | // index: string
136 | // fields: pos, touchIdChanged, byTouchId, prevTouchId
137 | int g_stringTouchState_heapAddr;
138 | int g_stringTouchState_numStrings;
139 | int g_stringTouchState_heapSizePerUnit;
140 | int g_stringTouchState_heapAddrOffset_pos;
141 | int g_stringTouchState_heapAddrOffset_touchIdChanged;
142 | int g_stringTouchState_heapAddrOffset_byTouchId;
143 | int g_stringTouchState_heapAddrOffset_prevTouchId;
144 |
145 | int stringTouchStateAddr(int stringIndex, int offset) {
146 | int addrBase = g_stringTouchState_heapAddr;
147 | addrBase += stringIndex * g_blockTouchState_heapSizePerUnit;
148 | return addrBase + offset;
149 | }
150 |
151 | int getStringTouchPos(int stringIndex) {
152 | return getHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_pos));
153 | }
154 |
155 | int getStringTouchIdChanged(int stringIndex) {
156 | return getHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_touchIdChanged));
157 | }
158 |
159 | int getStringTouchByTouchId(int stringIndex) {
160 | return getHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_byTouchId));
161 | }
162 |
163 | int getStringTouchPrevTouchId(int stringIndex) {
164 | return getHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_prevTouchId));
165 | }
166 |
167 | void setStringTouchPos(int stringIndex, int value) {
168 | setHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_pos), value);
169 | }
170 |
171 | void setStringTouchIdChanged(int stringIndex, int value) {
172 | setHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_touchIdChanged), value);
173 | }
174 |
175 | void setStringTouchByTouchId(int stringIndex, int value) {
176 | setHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_byTouchId), value);
177 | }
178 |
179 | void setStringTouchPrevTouchId(int stringIndex, int value) {
180 | setHeapInt(stringTouchStateAddr(stringIndex, g_stringTouchState_heapAddrOffset_prevTouchId), value);
181 | }
182 |
183 | // for sending MIDI notes (used only on main block)
184 | // index: touch_base_1_index
185 | // fields: midiNoteIsOn, midiNoteNum, midiNoteOnString, midiNoteLockPitchBend, midiNoteStartMicroTuning(float)
186 | int g_touchMidiNoteState_heapAddr;
187 | int g_touchMidiNoteState_numTouches;
188 | int g_touchMidiNoteState_heapSizePerUnit;
189 | int g_touchMidiNoteState_heapAddrOffset_midiNoteIsOn;
190 | int g_touchMidiNoteState_heapAddrOffset_midiNoteNum;
191 | int g_touchMidiNoteState_heapAddrOffset_midiNoteOnString;
192 | int g_touchMidiNoteState_heapAddrOffset_midiNoteLockPitchBend;
193 | int g_touchMidiNoteState_heapAddrOffset_midiNoteStartMicroTuningFloat;
194 |
195 | int stringTouchMidiNoteStateAddr(int touchIndex, int offset) {
196 | int addrBase = g_touchMidiNoteState_heapAddr;
197 | addrBase += (touchIndex-1) * g_touchMidiNoteState_heapSizePerUnit;
198 | return addrBase + offset;
199 | }
200 |
201 | int getTouchMidiNoteIsOn(int touchIndex) {
202 | return getHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteIsOn));
203 | }
204 |
205 | int getTouchMidiNoteNum(int touchIndex) {
206 | return getHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteNum));
207 | }
208 |
209 | int getTouchMidiNoteOnString(int touchIndex) {
210 | return getHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteOnString));
211 | }
212 |
213 | int getTouchMidiNoteLockPitchBend(int touchIndex) {
214 | return getHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteLockPitchBend));
215 | }
216 |
217 | float getTouchMidiNoteStartMicroTuning(int touchIndex) {
218 | return float(getHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteStartMicroTuningFloat))) / FLOAT_SCALE_TO_INT;
219 | }
220 |
221 | void setTouchMidiNoteIsOn(int touchIndex, int value) {
222 | setHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteIsOn), value);
223 | }
224 |
225 | void setTouchMidiNoteNum(int touchIndex, int value) {
226 | setHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteNum), value);
227 | }
228 |
229 | void setTouchMidiNoteOnString(int touchIndex, int value) {
230 | setHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteOnString), value);
231 | }
232 |
233 | void setTouchMidiNoteLockPitchBend(int touchIndex, int value) {
234 | setHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteLockPitchBend), value);
235 | }
236 |
237 | void setTouchMidiNoteStartMicroTuning(int touchIndex, float fvalue) {
238 | int value = int(fvalue * FLOAT_SCALE_TO_INT);
239 | setHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchMidiNoteState_heapAddrOffset_midiNoteStartMicroTuningFloat), value);
240 | }
241 |
242 | // block ID table (used only on main block)
243 | // index: blocks
244 | // fields: id
245 | int g_blockState_heapAddr;
246 | int g_blockState_numBlocks;
247 | int g_blockState_heapSizePerUnit;
248 | int g_blockState_heapAddrOffset_id;
249 |
250 | int blockStateAddr(int blockIndex, int offset) {
251 | int addrBase = g_blockState_heapAddr;
252 | addrBase += blockIndex * g_blockState_heapSizePerUnit;
253 | return addrBase + offset;
254 | }
255 |
256 | int getBlockIdForIndex(int blockIndex)
257 | {
258 | return getHeapInt(blockStateAddr(blockIndex, g_blockState_heapAddrOffset_id));
259 | }
260 |
261 | void setBlockIdForIndex(int blockIndex, int value)
262 | {
263 | setHeapInt(blockStateAddr(blockIndex, g_blockState_heapAddrOffset_id), value);
264 | }
265 |
266 | // touch start position on each block
267 | // index: touches
268 | // fields: startX, startY
269 | int g_touchState_heapAddr;
270 | int g_touchState_numTouches;
271 | int g_touchState_heapSizePerUnit;
272 | int g_touchState_heapAddrOffset_startX;
273 | int g_touchState_heapAddrOffset_startY;
274 |
275 | int touchStateAddr(int touchIndex, int offset) {
276 | int addrBase = g_touchState_heapAddr;
277 | addrBase += (touchIndex-1) * g_touchState_heapSizePerUnit;
278 | return addrBase + offset;
279 | }
280 |
281 | int getStartXForTouch(int touchIndex) {
282 | return getHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchState_heapAddrOffset_startX));
283 | }
284 |
285 | int getStartYForTouch(int touchIndex) {
286 | return getHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchState_heapAddrOffset_startY));
287 | }
288 |
289 | void setStartXForTouch(int touchIndex, int value) {
290 | setHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchState_heapAddrOffset_startX), value);
291 | }
292 |
293 | void setStartYForTouch(int touchIndex, int value) {
294 | setHeapInt(stringTouchMidiNoteStateAddr(touchIndex, g_touchState_heapAddrOffset_startY), value);
295 | }
296 |
297 | // global variables
298 |
299 | int g_masterBlockId;
300 | int g_distanceToMasterBlock;
301 |
302 | // CONSTANTS
303 |
304 | int MAX_NUM_BLOCKS;
305 | int MAX_FRETS;
306 | int NUM_TOUCHES;
307 | int FLOAT_SCALE_TO_INT;
308 |
309 | int MESSAGE_HELLO;
310 | int MESSAGE_ASSIGN_INDEX;
311 | int MESSAGE_TOUCH_START;
312 | int MESSAGE_TOUCH_MOVE;
313 | int MESSAGE_TOUCH_END;
314 | int MESSAGE_DRAW_STRING_POS;
315 | int MESSAGE_DRAW_STRING_PLAYING;
316 |
317 |
318 | void initialize_global_variables() {
319 | g_masterBlockId = 0;
320 | g_distanceToMasterBlock = -1;
321 |
322 | // limited by heap size
323 | MAX_NUM_BLOCKS = 3;
324 | NUM_TOUCHES = 4;
325 |
326 | FLOAT_SCALE_TO_INT = 20000;
327 |
328 | MAX_FRETS = 24;
329 |
330 | MESSAGE_HELLO = 1000;
331 | MESSAGE_ASSIGN_INDEX = 1001;
332 | MESSAGE_TOUCH_START = 2000;
333 | MESSAGE_TOUCH_MOVE = 2100;
334 | MESSAGE_TOUCH_END = 2200;
335 | MESSAGE_DRAW_STRING_POS = 3000;
336 | MESSAGE_DRAW_STRING_PLAYING = 3001;
337 |
338 | int heapAddr = 0;
339 |
340 | g_blockState_heapAddr = heapAddr;
341 | g_blockState_numBlocks = MAX_NUM_BLOCKS;
342 | g_blockState_heapSizePerUnit = 4;
343 | g_blockState_heapAddrOffset_id = 0;
344 | heapAddr += g_blockState_numBlocks * g_blockState_heapSizePerUnit;
345 |
346 | g_touchState_heapAddr = heapAddr;
347 | g_touchState_numTouches = NUM_TOUCHES;
348 | g_touchState_heapSizePerUnit = 8;
349 | g_touchState_heapAddrOffset_startX = 0;
350 | g_touchState_heapAddrOffset_startY = 4;
351 | heapAddr += g_touchState_numTouches * g_touchState_heapSizePerUnit;
352 |
353 | g_blockTouchState_heapAddr = heapAddr;
354 | g_blockTouchState_numBlocks = MAX_NUM_BLOCKS;
355 | g_blockTouchState_numTouches = NUM_TOUCHES;
356 | g_blockTouchState_heapSizePerUnit = 16;
357 | g_blockTouchState_heapAddrOffset_isDown = 0;
358 | g_blockTouchState_heapAddrOffset_x = 4;
359 | g_blockTouchState_heapAddrOffset_y = 8;
360 | g_blockTouchState_heapAddrOffset_z = 12;
361 | heapAddr += g_blockTouchState_numBlocks * g_blockTouchState_numTouches * g_blockTouchState_heapSizePerUnit;
362 |
363 | g_stringDrawState_heapAddr = heapAddr;
364 | g_stringDrawState_numStrings = conf_numStrings;
365 | g_stringDrawState_heapSizePerUnit = 12;
366 | g_stringDrawState_heapAddrOffset_isDown = 0;
367 | g_stringDrawState_heapAddrOffset_drawPos = 4;
368 | g_stringDrawState_heapAddrOffset_isPlaying = 8;
369 | heapAddr += g_stringDrawState_numStrings * g_stringDrawState_heapSizePerUnit;
370 |
371 | g_stringTouchState_heapAddr = 296;
372 | g_stringTouchState_numStrings = conf_numStrings;
373 | g_stringTouchState_heapSizePerUnit = 16;
374 | g_stringTouchState_heapAddrOffset_pos = 0;
375 | g_stringTouchState_heapAddrOffset_touchIdChanged = 4;
376 | g_stringTouchState_heapAddrOffset_byTouchId = 8;
377 | g_stringTouchState_heapAddrOffset_prevTouchId = 12;
378 | heapAddr += g_stringTouchState_numStrings * g_stringTouchState_heapSizePerUnit;
379 |
380 | g_touchMidiNoteState_heapAddr = 376;
381 | g_touchMidiNoteState_numTouches = NUM_TOUCHES;
382 | g_touchMidiNoteState_heapSizePerUnit = 20;
383 | g_touchMidiNoteState_heapAddrOffset_midiNoteIsOn = 0;
384 | g_touchMidiNoteState_heapAddrOffset_midiNoteNum = 4;
385 | g_touchMidiNoteState_heapAddrOffset_midiNoteOnString = 8;
386 | g_touchMidiNoteState_heapAddrOffset_midiNoteLockPitchBend = 12;
387 | g_touchMidiNoteState_heapAddrOffset_midiNoteStartMicroTuningFloat = 16;
388 | heapAddr += g_touchMidiNoteState_numTouches * g_touchMidiNoteState_heapSizePerUnit;
389 |
390 | log(heapAddr);
391 | }
392 |
393 | void resetStringPos()
394 | {
395 | for (int i=0; i= 3) {
403 | return 0.125 * (2 * 3 + 3 * (stringIndex-2)) + 0.125;
404 | } else {
405 | return 0.125 * (2 * (1+stringIndex)) + 0.0625;
406 | }
407 | }
408 | void updateTouchForString(int blockIndex, int touchIndex, int x, int y, int z)
409 | {
410 | int stringPos = (getNumBlocksInTopology() - blockIndex) * 2 * FLOAT_SCALE_TO_INT - y;
411 | float floatX = float(x) / float(FLOAT_SCALE_TO_INT);
412 | int touchId = blockIndex * 100 + touchIndex;
413 | for (int i=0; i getStringTouchPos(i)) {
416 | setStringTouchPos(i, stringPos);
417 | setStringTouchByTouchId(i, touchId);
418 | }
419 | break;
420 | }
421 | }
422 | }
423 |
424 | void printStringPos()
425 | {
426 | for (int i=0; i 0 ? microTuning : microTuning - 0.5;
480 | return float(round(temp*2))/2;
481 | }
482 |
483 | void sendNote(int action, int touchIndex, float x, float y, float z) {
484 | if (touchIndex > 5) {
485 | return;
486 | }
487 |
488 | for (int i=0; i= g_stringTouchState_numStrings) {
512 | return;
513 | }
514 |
515 | int midiNoteIsOn = (action != MESSAGE_TOUCH_END) ? 1 : 0;
516 | int pressureValue = min(127, int(127 * (conf_pressureBoost + z * conf_pressureScale)));
517 | int touchOrStringChanged = origStringIndex != stringIndex || getStringTouchIdChanged(stringIndex);
518 |
519 | int midiNoteNum = 0;
520 | if (midiNoteIsOn) {
521 | if (origMidiNoteIsOn && !touchOrStringChanged) {
522 | midiNoteNum = origMidiNoteNum;
523 | } else {
524 | midiNoteNum = (12 + baseMidiNum + round(midiNumOffset));
525 | }
526 | }
527 | float midiNoteMicroTuning = midiNumOffset - float(midiNoteNum - baseMidiNum - 12);
528 | if (midiNoteIsOn && !touchOrStringChanged) {
529 | midiNoteMicroTuning = midiNumOffset - float(origMidiNoteNum - baseMidiNum - 12);
530 | }
531 | if (midiNoteIsOn == 0 || (touchOrStringChanged && origMidiNoteIsOn)) {
532 | sendNoteOff(channel, origMidiNoteNum, 0);
533 | }
534 | if (origMidiNoteIsOn == 0 || (touchOrStringChanged && midiNoteIsOn)) {
535 | sendNoteOn(channel, midiNoteNum, pressureValue);
536 | }
537 |
538 | // SWAM cello will receive pressure as aftertouch
539 | sendChannelPressure(channel, pressureValue);
540 |
541 | int slideValue = max(0, 64 + int((0.003 / getMaxYForBowingArea()) * ((y * FLOAT_SCALE_TO_INT) - getStartYForTouch(touchIndex))));
542 | sendCC(channel, 74, min(127, slideValue));
543 |
544 | int pitchBendValue = 8192;
545 | int midiNoteLockPitchBend = origMidiNoteLockPitchBend;
546 | float tuningDiff = midiNoteMicroTuning - origMidiNoteStartMicroTuning;
547 | float tuningLimit = float(48);
548 | float semitoneTuningLimit = 8192.0 / tuningLimit;
549 | if (origMidiNoteNum == midiNoteNum) {
550 | if (midiNoteLockPitchBend == 1 && abs(tuningDiff) >= conf_vibratoThreshold) {
551 | midiNoteLockPitchBend = 0;
552 | }
553 | if (midiNoteLockPitchBend == 0) {
554 | float tuningDiffToSemitoneAbove = midiNoteMicroTuning - autoTuned(origMidiNoteStartMicroTuning+0.5);
555 |
556 | float tuningDiffToSemitoneBelow = midiNoteMicroTuning - autoTuned(origMidiNoteStartMicroTuning-0.5);
557 | if (origMidiNoteStartMicroTuning - autoTuned(origMidiNoteStartMicroTuning) < 0) {
558 | tuningDiffToSemitoneAbove = midiNoteMicroTuning - autoTuned(origMidiNoteStartMicroTuning);
559 | } else if (origMidiNoteStartMicroTuning - autoTuned(origMidiNoteStartMicroTuning) > 0) {
560 | tuningDiffToSemitoneBelow = midiNoteMicroTuning - autoTuned(origMidiNoteStartMicroTuning);
561 | }
562 | float tuningDiffToAutotunedOrig = midiNoteMicroTuning - autoTuned(origMidiNoteStartMicroTuning);
563 | if (tuningDiff > 0 && tuningDiff < tuningDiffToSemitoneAbove) {
564 | pitchBendValue = 8192 + int(8192.0 / 4.0 * tuningDiffToSemitoneAbove / (0.5 - origMidiNoteStartMicroTuning));
565 | } else if (tuningDiff > tuningDiffToSemitoneBelow && tuningDiff < 0) {
566 | pitchBendValue = 8192 + int(8192.0 / 4.0 * tuningDiffToSemitoneBelow / (0.5 + origMidiNoteStartMicroTuning));
567 | } else {
568 | pitchBendValue = 8192 + int(tuningDiffToAutotunedOrig * semitoneTuningLimit);
569 | }
570 | }
571 | }
572 |
573 | if (midiNoteIsOn) {
574 | sendPitchBend(channel, min(16383, pitchBendValue));
575 | }
576 |
577 | stringIndex = (midiNoteIsOn) ? stringIndex : 5;
578 | setTouchMidiNoteIsOn(touchIndex, midiNoteIsOn);
579 | setTouchMidiNoteNum(touchIndex, midiNoteNum);
580 | setTouchMidiNoteOnString(touchIndex, stringIndex);
581 | setTouchMidiNoteLockPitchBend(touchIndex, midiNoteLockPitchBend);
582 | if (touchOrStringChanged && midiNoteIsOn == 1) {
583 | setTouchMidiNoteLockPitchBend(touchIndex, (!touchOrStringChanged) ? 0 : 1);
584 | setTouchMidiNoteStartMicroTuning(touchIndex, (!touchOrStringChanged) ? 0.0 : midiNoteMicroTuning);
585 | }
586 | updateStringPlayingState();
587 |
588 | for (int i=0; i= 3) ? (7 + 3 * (i - 3)) : (1 + 2 * i);
701 | int stringWidth = (conf_fatLowStrings && i >= 3) ? 2 : 1;
702 | fillRect(baseStringColor, stringPosX, 0, stringWidth, 15);
703 | }
704 |
705 | // draw string pos
706 | int stringColor = conf_stringColor;
707 | int playingColor = conf_stringPlayingColor;
708 | for (int i=0; i= 3) ? (7 + 3 * (i - 3)) : (1 + 2 * i);
712 | int stringWidth = (conf_fatLowStrings && i >= 3) ? 2 : 1;
713 | fillRect(getStringIsPlaying(i) ? playingColor : stringColor, stringPosX, 0, stringWidth, y);
714 | }
715 | }
716 |
717 | // draw pressure map
718 | drawPressureMap();
719 | fadePressureMap();
720 | }
721 |
722 | void handleButtonDown (int index)
723 | {
724 |
725 | }
726 |
727 | void handleButtonUp (int index)
728 | {
729 |
730 | }
731 |
732 |
733 | int encodeToData(float x, float y) {
734 | int intX = int(x * FLOAT_SCALE_TO_INT);
735 | int intY = int(y * FLOAT_SCALE_TO_INT);
736 | return intX * 2 * FLOAT_SCALE_TO_INT + intY;
737 | }
738 |
739 | float getMaxYForBowingArea() {
740 | if (getNumBlocksInTopology() >= 3) {
741 | return 1.2;
742 | }
743 | return 0.4;
744 | }
745 | // touch events parameter range
746 | // x: from 0.07 to 1.93
747 | // y: from 0.07 to 1.93
748 | // z: from 0.0 to 1.0
749 | // vz: from 0.0 to 1.0
750 |
751 | void touchStart (int index, float x, float y, float z, float vz)
752 | {
753 | setStartXForTouch(index, int(x * FLOAT_SCALE_TO_INT));
754 | setStartYForTouch(index, int(y * FLOAT_SCALE_TO_INT));
755 | if (g_distanceToMasterBlock == 0 && getStartYForTouch(index) < int(getMaxYForBowingArea() * FLOAT_SCALE_TO_INT)) {
756 | addPressurePoint (conf_bowingFingerColor, x, y, z * 50);
757 | sendNote(MESSAGE_TOUCH_START, index, x, y, z);
758 | return;
759 | }
760 | addPressurePoint (conf_stringFingerColor, x, y, z * 10);
761 | sendMessageToBlockOrSelf(g_masterBlockId,
762 | MESSAGE_TOUCH_START + g_distanceToMasterBlock,
763 | index,
764 | encodeToData(x, y));
765 | }
766 |
767 | void touchMove (int index, float x, float y, float z, float vz)
768 | {
769 | if (g_distanceToMasterBlock == 0 && getStartYForTouch(index) < int(getMaxYForBowingArea() * FLOAT_SCALE_TO_INT)) {
770 | addPressurePoint (conf_bowingFingerColor, x, y, z * 50);
771 | sendNote(MESSAGE_TOUCH_MOVE, index, x, y, z);
772 | return;
773 | }
774 | addPressurePoint (conf_stringFingerColor, x, y, z * 10);
775 | sendMessageToBlockOrSelf(g_masterBlockId,
776 | MESSAGE_TOUCH_MOVE + g_distanceToMasterBlock,
777 | index,
778 | encodeToData(x, y));
779 | }
780 |
781 | void touchEnd (int index, float x, float y, float z, float vz)
782 | {
783 | if (g_distanceToMasterBlock == 0 && getStartYForTouch(index) < int(getMaxYForBowingArea() * FLOAT_SCALE_TO_INT)) {
784 | sendNote(MESSAGE_TOUCH_END, index, x, y, z);
785 | return;
786 | }
787 | sendMessageToBlockOrSelf(g_masterBlockId,
788 | MESSAGE_TOUCH_END + g_distanceToMasterBlock,
789 | index,
790 | encodeToData(x, y));
791 | // XXX: send touch end again to avoid message dropped
792 | sendMessageToBlockOrSelf(g_masterBlockId,
793 | MESSAGE_TOUCH_END + g_distanceToMasterBlock,
794 | index,
795 | encodeToData(x, y));
796 | // XXX: send touch end again to avoid message dropped
797 | sendMessageToBlockOrSelf(g_masterBlockId,
798 | MESSAGE_TOUCH_END + g_distanceToMasterBlock,
799 | index,
800 | encodeToData(x, y));
801 | }
802 |
803 | void handleMessage (int data0, int data1, int data2)
804 | {
805 | int data0_type = data0 - (data0 % 100);
806 | if (data0 == MESSAGE_HELLO) {
807 | // data1: reporter block ID
808 | // data2: block index (distance accumulated from tail block)
809 | if (isMasterBlock()) {
810 | setBlockIdForIndex(data2, data1);
811 | // send master block ID and distance value (as index) back to reporter block
812 | sendMessageToBlock(data1, MESSAGE_ASSIGN_INDEX, getBlockIDForIndex(0), data2);
813 | } else {
814 | // pass the message towards master block with distance value incremented
815 | sendMessageToBlock(getBlockIDOnPort(getPortToMaster()), MESSAGE_HELLO, data1, data2+1);
816 | }
817 | } else if (data0 == MESSAGE_ASSIGN_INDEX) {
818 | // data1: master block ID
819 | // data2: self block index / distance value
820 | g_masterBlockId = data1;
821 | g_distanceToMasterBlock = data2;
822 | } else if (data0_type == MESSAGE_TOUCH_START
823 | || data0_type == MESSAGE_TOUCH_MOVE
824 | || data0_type == MESSAGE_TOUCH_END) {
825 | // data0: MESSAGE_TYPE (2000/2100/2200) + block index (0-99)
826 | // data1: touch index
827 | // data2: touchX * (2 * FLOAT_SCALE_TO_INT) + touchY
828 | int block_index = data0 % 100;
829 | int x = data2 / (2 * FLOAT_SCALE_TO_INT);
830 | int y = data2 % (2 * FLOAT_SCALE_TO_INT);
831 | setTouchForStringPos(block_index, data0_type, data1, x, y, FLOAT_SCALE_TO_INT);
832 | } else if (data0 == MESSAGE_DRAW_STRING_POS) {
833 | // data1: string index;
834 | // data2: string pos;
835 | int isDown = 0;
836 | int drawPos = 0;
837 | int posBlockIndex = getNumBlocksInTopology() - 1 - (data2 / (2 * FLOAT_SCALE_TO_INT));
838 | if (posBlockIndex == g_distanceToMasterBlock) {
839 | isDown = 1;
840 | drawPos = data2 % (2 * FLOAT_SCALE_TO_INT);
841 | } else if (posBlockIndex > g_distanceToMasterBlock) {
842 | isDown = 1;
843 | drawPos = 0;
844 | }
845 | setStringIsDown(data1, isDown);
846 | setStringDrawPos(data1, drawPos);
847 | } else if (data0 == MESSAGE_DRAW_STRING_PLAYING) {
848 | // data1: string index
849 | // data2: isPlaying value
850 | setStringIsPlaying(data1, data2);
851 | }
852 | }
853 |
854 | void setTouchForStringPos(int blockDistance, int action, int index, int x, int y, int z)
855 | {
856 | if (action == MESSAGE_TOUCH_END) {
857 | setBlockTouchIsDown(blockDistance, index, 0);
858 | } else {
859 | setBlockTouchIsDown(blockDistance, index, 1);
860 | setBlockTouchX(blockDistance, index, x);
861 | setBlockTouchY(blockDistance, index, y);
862 | setBlockTouchZ(blockDistance, index, z);
863 | }
864 |
865 | updateStringPos();
866 | }
867 |
868 | void updateStringPos()
869 | {
870 | resetStringPos();
871 |
872 | for (int i=0; i