├── 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