Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add native spreadnob UI and M4L download rail

+4597 -482
ac-m4l/AC Metronome.amxd

This is a binary file and will not be displayed.

ac-m4l/AC Notepat.amxd

This is a binary file and will not be displayed.

ac-m4l/AC Prompt.amxd

This is a binary file and will not be displayed.

+3584 -222
ac-m4l/AC-KnobMap.amxd.json
··· 9 9 "modernui": 1 10 10 }, 11 11 "classnamespace": "box", 12 - "rect": [100.0, 100.0, 950.0, 550.0], 13 - "openrect": [0.0, 0.0, 250.0, 170.0], 12 + "rect": [ 13 + 100.0, 14 + 100.0, 15 + 950.0, 16 + 550.0 17 + ], 18 + "openrect": [ 19 + 0.0, 20 + 0.0, 21 + 250.0, 22 + 194.0 23 + ], 14 24 "openinpresentation": 1, 15 - "gridsize": [15.0, 15.0], 25 + "gridsize": [ 26 + 15.0, 27 + 15.0 28 + ], 16 29 "enablehscroll": 0, 17 30 "enablevscroll": 0, 18 31 "devicewidth": 250.0, 19 - "description": "Spreadnob — spread any knob across your MIDI keys", 32 + "description": "Spreadnob \u2014 spread any knob across your MIDI keys", 20 33 "boxes": [ 21 34 { 22 35 "box": { ··· 24 37 "maxclass": "panel", 25 38 "numinlets": 1, 26 39 "numoutlets": 0, 27 - "patching_rect": [700.0, 30.0, 250.0, 170.0], 40 + "patching_rect": [ 41 + 700.0, 42 + 30.0, 43 + 250.0, 44 + 170.0 45 + ], 28 46 "presentation": 1, 29 - "presentation_rect": [0.0, 0.0, 250.0, 170.0], 47 + "presentation_rect": [ 48 + 0.0, 49 + 0.0, 50 + 250.0, 51 + 194.0 52 + ], 53 + "background": 1, 54 + "ignoreclick": 1, 30 55 "rounded": 0, 31 56 "mode": 0, 32 - "bgcolor": [0.114, 0.106, 0.18, 1.0] 57 + "bgcolor": [ 58 + 0.06, 59 + 0.047, 60 + 0.09, 61 + 1.0 62 + ] 63 + } 64 + }, 65 + { 66 + "box": { 67 + "id": "obj-jweb", 68 + "maxclass": "jweb~", 69 + "disablefind": 0, 70 + "latency": 0, 71 + "numinlets": 1, 72 + "numoutlets": 3, 73 + "outlettype": [ 74 + "signal", 75 + "signal", 76 + "" 77 + ], 78 + "patching_rect": [ 79 + 700.0, 80 + 30.0, 81 + 250.0, 82 + 194.0 83 + ], 84 + "presentation": 1, 85 + "presentation_rect": [ 86 + 0.0, 87 + 0.0, 88 + 250.0, 89 + 194.0 90 + ], 91 + "rendermode": 1, 92 + "url": "https://localhost:8888/spreadnob?daw=1&nogap=true&width=250&height=194" 93 + } 94 + }, 95 + { 96 + "box": { 97 + "id": "obj-jweb-route", 98 + "maxclass": "newobj", 99 + "numinlets": 1, 100 + "numoutlets": 6, 101 + "outlettype": [ 102 + "", 103 + "", 104 + "", 105 + "", 106 + "", 107 + "" 108 + ], 109 + "patching_rect": [ 110 + 700.0, 111 + 240.0, 112 + 210.0, 113 + 22.0 114 + ], 115 + "text": "route ready range log error warn" 116 + } 117 + }, 118 + { 119 + "box": { 120 + "id": "obj-jweb-activate", 121 + "maxclass": "message", 122 + "numinlets": 2, 123 + "numoutlets": 1, 124 + "outlettype": [ 125 + "" 126 + ], 127 + "patching_rect": [ 128 + 700.0, 129 + 270.0, 130 + 60.0, 131 + 22.0 132 + ], 133 + "text": "activate 1" 134 + } 135 + }, 136 + { 137 + "box": { 138 + "id": "obj-jweb-print", 139 + "maxclass": "newobj", 140 + "numinlets": 1, 141 + "numoutlets": 0, 142 + "patching_rect": [ 143 + 770.0, 144 + 270.0, 145 + 105.0, 146 + 22.0 147 + ], 148 + "text": "print [AC-JWEB]" 149 + } 150 + }, 151 + { 152 + "box": { 153 + "id": "obj-jweb-range", 154 + "maxclass": "newobj", 155 + "numinlets": 1, 156 + "numoutlets": 2, 157 + "outlettype": [ 158 + "int", 159 + "int" 160 + ], 161 + "patching_rect": [ 162 + 885.0, 163 + 270.0, 164 + 70.0, 165 + 22.0 166 + ], 167 + "text": "unpack 0 0" 33 168 } 34 169 }, 35 170 { ··· 38 173 "maxclass": "comment", 39 174 "numinlets": 1, 40 175 "numoutlets": 0, 41 - "patching_rect": [700.0, 32.0, 100.0, 20.0], 42 - "presentation": 1, 43 - "presentation_rect": [10.0, 8.0, 95.0, 20.0], 44 - "text": "AC SPREAD NOB", 45 - "fontsize": 13.0, 176 + "patching_rect": [ 177 + 700.0, 178 + 32.0, 179 + 100.0, 180 + 20.0 181 + ], 182 + "presentation": 0, 183 + "presentation_rect": [ 184 + 10.0, 185 + 8.0, 186 + 120.0, 187 + 22.0 188 + ], 189 + "text": "spreadnob", 190 + "fontname": "YWFTProcessing-Regular", 191 + "fontsize": 16.0, 46 192 "fontface": 1, 47 - "textcolor": [0.875, 0.863, 0.922, 1.0] 193 + "textcolor": [ 194 + 1.0, 195 + 0.486, 196 + 0.722, 197 + 1.0 198 + ] 48 199 } 49 200 }, 50 201 { ··· 53 204 "maxclass": "comment", 54 205 "numinlets": 1, 55 206 "numoutlets": 0, 56 - "patching_rect": [810.0, 32.0, 50.0, 18.0], 57 - "presentation": 1, 58 - "presentation_rect": [200.0, 11.0, 42.0, 18.0], 207 + "patching_rect": [ 208 + 810.0, 209 + 32.0, 210 + 50.0, 211 + 18.0 212 + ], 213 + "presentation": 0, 214 + "presentation_rect": [ 215 + 186.0, 216 + 11.0, 217 + 56.0, 218 + 18.0 219 + ], 59 220 "text": "v1.0.4", 221 + "fontname": "YWFTProcessing-Regular", 60 222 "fontsize": 10.0, 61 - "textcolor": [0.486, 0.467, 0.584, 1.0] 223 + "textcolor": [ 224 + 0.69, 225 + 0.65, 226 + 0.77, 227 + 1.0 228 + ] 62 229 } 63 230 }, 64 231 { ··· 67 234 "maxclass": "live.text", 68 235 "numinlets": 1, 69 236 "numoutlets": 2, 70 - "outlettype": ["", ""], 71 - "patching_rect": [200.0, 30.0, 56.0, 20.0], 72 - "presentation": 1, 73 - "presentation_rect": [10.0, 38.0, 56.0, 20.0], 74 - "parameter_enable": 1, 237 + "outlettype": [ 238 + "", 239 + "" 240 + ], 241 + "patching_rect": [ 242 + 200.0, 243 + 30.0, 244 + 56.0, 245 + 20.0 246 + ], 247 + "presentation": 0, 248 + "presentation_rect": [ 249 + 10.0, 250 + 38.0, 251 + 56.0, 252 + 20.0 253 + ], 254 + "parameter_enable": 0, 75 255 "saved_attribute_attributes": { 76 256 "valueof": { 77 257 "parameter_longname": "Grab", 78 258 "parameter_shortname": "Grab", 79 259 "parameter_type": 2, 80 - "parameter_enum": ["GRAB", "GRAB"], 260 + "parameter_enum": [ 261 + "GRAB", 262 + "GRAB" 263 + ], 81 264 "parameter_mmax": 1.0 82 265 } 83 266 }, 84 267 "varname": "Grab", 85 268 "mode": 0, 86 269 "text": "GRAB", 87 - "texton": "GRAB" 270 + "texton": "GRAB", 271 + "textcolor": [ 272 + 0.96, 273 + 0.95, 274 + 1.0, 275 + 1.0 276 + ], 277 + "bgcolor": [ 278 + 0.18, 279 + 0.12, 280 + 0.18, 281 + 1.0 282 + ], 283 + "bgoncolor": [ 284 + 1.0, 285 + 0.486, 286 + 0.722, 287 + 1.0 288 + ] 88 289 } 89 290 }, 90 291 { ··· 93 294 "maxclass": "message", 94 295 "numinlets": 2, 95 296 "numoutlets": 1, 96 - "outlettype": [""], 97 - "patching_rect": [700.0, 65.0, 170.0, 22.0], 98 - "presentation": 1, 99 - "presentation_rect": [72.0, 40.0, 168.0, 18.0], 100 - "text": "Click a knob then GRAB", 101 - "textcolor": [0.588, 0.494, 0.761, 1.0], 102 - "bgcolor": [0.0, 0.0, 0.0, 0.0], 297 + "outlettype": [ 298 + "" 299 + ], 300 + "patching_rect": [ 301 + 700.0, 302 + 65.0, 303 + 170.0, 304 + 22.0 305 + ], 306 + "presentation": 0, 307 + "presentation_rect": [ 308 + 72.0, 309 + 40.0, 310 + 168.0, 311 + 18.0 312 + ], 313 + "ignoreclick": 1, 314 + "text": "click a knob, then play qwerty", 315 + "fontname": "YWFTProcessing-Regular", 316 + "textcolor": [ 317 + 1.0, 318 + 0.694, 319 + 0.831, 320 + 1.0 321 + ], 322 + "bgcolor": [ 323 + 0.0, 324 + 0.0, 325 + 0.0, 326 + 0.0 327 + ], 103 328 "fontsize": 10.0 104 329 } 105 330 }, 106 331 { 107 332 "box": { 333 + "id": "obj-thisdevice", 334 + "maxclass": "newobj", 335 + "numinlets": 1, 336 + "numoutlets": 1, 337 + "outlettype": [ 338 + "bang" 339 + ], 340 + "patching_rect": [ 341 + 30.0, 342 + 30.0, 343 + 85.0, 344 + 22.0 345 + ], 346 + "text": "live.thisdevice" 347 + } 348 + }, 349 + { 350 + "box": { 108 351 "id": "obj-line1", 109 352 "maxclass": "panel", 110 353 "numinlets": 1, 111 354 "numoutlets": 0, 112 - "patching_rect": [700.0, 92.0, 230.0, 1.0], 113 - "presentation": 1, 114 - "presentation_rect": [10.0, 66.0, 230.0, 1.0], 115 - "bgcolor": [0.3, 0.278, 0.38, 1.0], 355 + "patching_rect": [ 356 + 700.0, 357 + 92.0, 358 + 230.0, 359 + 1.0 360 + ], 361 + "presentation": 0, 362 + "presentation_rect": [ 363 + 10.0, 364 + 66.0, 365 + 230.0, 366 + 2.0 367 + ], 368 + "bgcolor": [ 369 + 1.0, 370 + 0.486, 371 + 0.722, 372 + 0.85 373 + ], 116 374 "mode": 0, 117 375 "rounded": 0 118 376 } ··· 123 381 "maxclass": "comment", 124 382 "numinlets": 1, 125 383 "numoutlets": 0, 126 - "patching_rect": [700.0, 96.0, 42.0, 18.0], 127 - "presentation": 1, 128 - "presentation_rect": [10.0, 74.0, 42.0, 16.0], 129 - "text": "Range", 384 + "patching_rect": [ 385 + 700.0, 386 + 96.0, 387 + 42.0, 388 + 18.0 389 + ], 390 + "presentation": 0, 391 + "presentation_rect": [ 392 + 10.0, 393 + 74.0, 394 + 42.0, 395 + 16.0 396 + ], 397 + "text": "target", 398 + "fontname": "YWFTProcessing-Regular", 130 399 "fontsize": 10.0, 131 - "textcolor": [0.486, 0.467, 0.584, 1.0] 400 + "textcolor": [ 401 + 0.69, 402 + 0.65, 403 + 0.77, 404 + 1.0 405 + ] 132 406 } 133 407 }, 134 408 { ··· 137 411 "maxclass": "message", 138 412 "numinlets": 2, 139 413 "numoutlets": 1, 140 - "outlettype": [""], 141 - "patching_rect": [700.0, 116.0, 170.0, 22.0], 142 - "presentation": 1, 143 - "presentation_rect": [52.0, 73.0, 188.0, 18.0], 144 - "text": "—", 145 - "textcolor": [0.588, 0.576, 0.686, 1.0], 146 - "bgcolor": [0.0, 0.0, 0.0, 0.0], 414 + "outlettype": [ 415 + "" 416 + ], 417 + "patching_rect": [ 418 + 700.0, 419 + 116.0, 420 + 170.0, 421 + 22.0 422 + ], 423 + "presentation": 0, 424 + "presentation_rect": [ 425 + 52.0, 426 + 73.0, 427 + 188.0, 428 + 18.0 429 + ], 430 + "ignoreclick": 1, 431 + "text": "\u2014", 432 + "fontname": "YWFTProcessing-Regular", 433 + "textcolor": [ 434 + 0.73, 435 + 0.94, 436 + 1.0, 437 + 1.0 438 + ], 439 + "bgcolor": [ 440 + 0.0, 441 + 0.0, 442 + 0.0, 443 + 0.0 444 + ], 147 445 "fontsize": 10.0 148 446 } 149 447 }, ··· 153 451 "maxclass": "comment", 154 452 "numinlets": 1, 155 453 "numoutlets": 0, 156 - "patching_rect": [700.0, 140.0, 40.0, 18.0], 157 - "presentation": 1, 158 - "presentation_rect": [10.0, 97.0, 42.0, 16.0], 159 - "text": "Value", 454 + "patching_rect": [ 455 + 700.0, 456 + 140.0, 457 + 40.0, 458 + 18.0 459 + ], 460 + "presentation": 0, 461 + "presentation_rect": [ 462 + 10.0, 463 + 97.0, 464 + 42.0, 465 + 16.0 466 + ], 467 + "text": "value", 468 + "fontname": "YWFTProcessing-Regular", 160 469 "fontsize": 10.0, 161 - "textcolor": [0.486, 0.467, 0.584, 1.0] 470 + "textcolor": [ 471 + 0.69, 472 + 0.65, 473 + 0.77, 474 + 1.0 475 + ] 162 476 } 163 477 }, 164 478 { ··· 167 481 "maxclass": "message", 168 482 "numinlets": 2, 169 483 "numoutlets": 1, 170 - "outlettype": [""], 171 - "patching_rect": [700.0, 160.0, 80.0, 22.0], 172 - "presentation": 1, 173 - "presentation_rect": [52.0, 96.0, 70.0, 18.0], 174 - "text": "—", 175 - "textcolor": [0.875, 0.863, 0.922, 1.0], 176 - "bgcolor": [0.0, 0.0, 0.0, 0.0], 177 - "fontsize": 11.0, 484 + "outlettype": [ 485 + "" 486 + ], 487 + "patching_rect": [ 488 + 700.0, 489 + 160.0, 490 + 80.0, 491 + 22.0 492 + ], 493 + "presentation": 0, 494 + "presentation_rect": [ 495 + 52.0, 496 + 96.0, 497 + 88.0, 498 + 20.0 499 + ], 500 + "ignoreclick": 1, 501 + "text": "\u2014", 502 + "fontname": "YWFTProcessing-Regular", 503 + "textcolor": [ 504 + 1.0, 505 + 0.486, 506 + 0.722, 507 + 1.0 508 + ], 509 + "bgcolor": [ 510 + 0.0, 511 + 0.0, 512 + 0.0, 513 + 0.0 514 + ], 515 + "fontsize": 13.0, 178 516 "fontface": 1 179 517 } 180 518 }, ··· 184 522 "maxclass": "comment", 185 523 "numinlets": 1, 186 524 "numoutlets": 0, 187 - "patching_rect": [700.0, 182.0, 60.0, 18.0], 188 - "presentation": 1, 189 - "presentation_rect": [10.0, 120.0, 50.0, 16.0], 190 - "text": "Note", 525 + "patching_rect": [ 526 + 700.0, 527 + 182.0, 528 + 60.0, 529 + 18.0 530 + ], 531 + "presentation": 0, 532 + "presentation_rect": [ 533 + 10.0, 534 + 120.0, 535 + 50.0, 536 + 16.0 537 + ], 538 + "text": "note", 539 + "fontname": "YWFTProcessing-Regular", 191 540 "fontsize": 10.0, 192 - "textcolor": [0.486, 0.467, 0.584, 1.0] 541 + "textcolor": [ 542 + 0.69, 543 + 0.65, 544 + 0.77, 545 + 1.0 546 + ] 193 547 } 194 548 }, 195 549 { ··· 198 552 "maxclass": "message", 199 553 "numinlets": 2, 200 554 "numoutlets": 1, 201 - "outlettype": [""], 202 - "patching_rect": [760.0, 182.0, 60.0, 22.0], 203 - "presentation": 1, 204 - "presentation_rect": [52.0, 119.0, 40.0, 18.0], 205 - "text": "—", 206 - "textcolor": [0.875, 0.863, 0.922, 1.0], 207 - "bgcolor": [0.0, 0.0, 0.0, 0.0], 555 + "outlettype": [ 556 + "" 557 + ], 558 + "patching_rect": [ 559 + 760.0, 560 + 182.0, 561 + 60.0, 562 + 22.0 563 + ], 564 + "presentation": 0, 565 + "presentation_rect": [ 566 + 52.0, 567 + 119.0, 568 + 44.0, 569 + 18.0 570 + ], 571 + "ignoreclick": 1, 572 + "text": "\u2014", 573 + "fontname": "YWFTProcessing-Regular", 574 + "textcolor": [ 575 + 0.96, 576 + 0.95, 577 + 1.0, 578 + 1.0 579 + ], 580 + "bgcolor": [ 581 + 0.0, 582 + 0.0, 583 + 0.0, 584 + 0.0 585 + ], 208 586 "fontsize": 10.0 209 587 } 210 588 }, ··· 214 592 "maxclass": "comment", 215 593 "numinlets": 1, 216 594 "numoutlets": 0, 217 - "patching_rect": [850.0, 182.0, 100.0, 18.0], 218 - "presentation": 1, 219 - "presentation_rect": [117.0, 152.0, 130.0, 14.0], 595 + "patching_rect": [ 596 + 850.0, 597 + 182.0, 598 + 100.0, 599 + 18.0 600 + ], 601 + "presentation": 0, 602 + "presentation_rect": [ 603 + 108.0, 604 + 177.0, 605 + 136.0, 606 + 14.0 607 + ], 220 608 "text": "aesthetic.computer/ableton", 221 609 "clickableurl": "https://aesthetic.computer/ableton", 610 + "fontname": "YWFTProcessing-Regular", 222 611 "fontsize": 9.0, 223 - "textcolor": [0.35, 0.333, 0.447, 1.0] 612 + "textcolor": [ 613 + 0.47, 614 + 0.43, 615 + 0.57, 616 + 1.0 617 + ] 224 618 } 225 619 }, 226 620 { ··· 229 623 "maxclass": "comment", 230 624 "numinlets": 1, 231 625 "numoutlets": 0, 232 - "patching_rect": [700.0, 202.0, 40.0, 18.0], 233 - "presentation": 1, 234 - "presentation_rect": [10.0, 143.0, 40.0, 16.0], 235 - "text": "Mode", 626 + "patching_rect": [ 627 + 700.0, 628 + 202.0, 629 + 40.0, 630 + 18.0 631 + ], 632 + "presentation": 0, 633 + "presentation_rect": [ 634 + 10.0, 635 + 143.0, 636 + 40.0, 637 + 16.0 638 + ], 639 + "text": "raw", 640 + "fontname": "YWFTProcessing-Regular", 236 641 "fontsize": 10.0, 237 - "textcolor": [0.486, 0.467, 0.584, 1.0] 642 + "textcolor": [ 643 + 0.69, 644 + 0.65, 645 + 0.77, 646 + 1.0 647 + ] 238 648 } 239 649 }, 240 650 { ··· 243 653 "maxclass": "live.menu", 244 654 "numinlets": 1, 245 655 "numoutlets": 3, 246 - "outlettype": ["", "", "float"], 247 - "patching_rect": [430.0, 30.0, 100.0, 15.0], 248 - "presentation": 1, 249 - "presentation_rect": [52.0, 143.0, 85.0, 15.0], 656 + "outlettype": [ 657 + "", 658 + "", 659 + "float" 660 + ], 661 + "patching_rect": [ 662 + 430.0, 663 + 30.0, 664 + 100.0, 665 + 15.0 666 + ], 667 + "presentation": 0, 668 + "presentation_rect": [ 669 + 52.0, 670 + 143.0, 671 + 85.0, 672 + 15.0 673 + ], 674 + "hidden": 1, 250 675 "parameter_enable": 1, 251 676 "saved_attribute_attributes": { 252 677 "valueof": { 253 678 "parameter_longname": "Mode", 254 679 "parameter_shortname": "Mode", 255 680 "parameter_type": 2, 256 - "parameter_enum": ["Scaled", "Direct"], 681 + "parameter_enum": [ 682 + "Scaled", 683 + "Direct" 684 + ], 257 685 "parameter_mmax": 1.0 258 686 } 259 687 }, 260 688 "varname": "Mode" 261 689 } 262 690 }, 263 - 691 + { 692 + "box": { 693 + "id": "obj-midi-display", 694 + "maxclass": "message", 695 + "numinlets": 2, 696 + "numoutlets": 1, 697 + "outlettype": [ 698 + "" 699 + ], 700 + "patching_rect": [ 701 + 700.0, 702 + 206.0, 703 + 90.0, 704 + 22.0 705 + ], 706 + "presentation": 0, 707 + "presentation_rect": [ 708 + 52.0, 709 + 143.0, 710 + 86.0, 711 + 18.0 712 + ], 713 + "ignoreclick": 1, 714 + "text": "idle", 715 + "fontname": "YWFTProcessing-Regular", 716 + "textcolor": [ 717 + 0.96, 718 + 0.95, 719 + 1.0, 720 + 1.0 721 + ], 722 + "bgcolor": [ 723 + 0.0, 724 + 0.0, 725 + 0.0, 726 + 0.0 727 + ], 728 + "fontsize": 10.0 729 + } 730 + }, 731 + { 732 + "box": { 733 + "id": "obj-keys-label", 734 + "maxclass": "comment", 735 + "numinlets": 1, 736 + "numoutlets": 0, 737 + "patching_rect": [ 738 + 700.0, 739 + 230.0, 740 + 40.0, 741 + 18.0 742 + ], 743 + "presentation": 0, 744 + "presentation_rect": [ 745 + 10.0, 746 + 166.0, 747 + 38.0, 748 + 16.0 749 + ], 750 + "text": "keys", 751 + "fontname": "YWFTProcessing-Regular", 752 + "fontsize": 10.0, 753 + "textcolor": [ 754 + 0.69, 755 + 0.65, 756 + 0.77, 757 + 1.0 758 + ] 759 + } 760 + }, 761 + { 762 + "box": { 763 + "id": "obj-midi-low", 764 + "maxclass": "number", 765 + "numinlets": 1, 766 + "numoutlets": 2, 767 + "outlettype": [ 768 + "int", 769 + "bang" 770 + ], 771 + "patching_rect": [ 772 + 760.0, 773 + 230.0, 774 + 38.0, 775 + 22.0 776 + ], 777 + "presentation": 0, 778 + "presentation_rect": [ 779 + 52.0, 780 + 164.0, 781 + 34.0, 782 + 20.0 783 + ], 784 + "minimum": 0, 785 + "maximum": 127, 786 + "fontname": "YWFTProcessing-Regular", 787 + "fontsize": 10.0 788 + } 789 + }, 790 + { 791 + "box": { 792 + "id": "obj-keys-to-label", 793 + "maxclass": "comment", 794 + "numinlets": 1, 795 + "numoutlets": 0, 796 + "patching_rect": [ 797 + 804.0, 798 + 232.0, 799 + 18.0, 800 + 18.0 801 + ], 802 + "presentation": 0, 803 + "presentation_rect": [ 804 + 90.0, 805 + 166.0, 806 + 12.0, 807 + 16.0 808 + ], 809 + "text": "\u2014", 810 + "fontname": "YWFTProcessing-Regular", 811 + "fontsize": 10.0, 812 + "textcolor": [ 813 + 1.0, 814 + 0.486, 815 + 0.722, 816 + 1.0 817 + ] 818 + } 819 + }, 820 + { 821 + "box": { 822 + "id": "obj-midi-high", 823 + "maxclass": "number", 824 + "numinlets": 1, 825 + "numoutlets": 2, 826 + "outlettype": [ 827 + "int", 828 + "bang" 829 + ], 830 + "patching_rect": [ 831 + 826.0, 832 + 230.0, 833 + 38.0, 834 + 22.0 835 + ], 836 + "presentation": 0, 837 + "presentation_rect": [ 838 + 106.0, 839 + 164.0, 840 + 34.0, 841 + 20.0 842 + ], 843 + "minimum": 0, 844 + "maximum": 127, 845 + "fontname": "YWFTProcessing-Regular", 846 + "fontsize": 10.0 847 + } 848 + }, 849 + { 850 + "box": { 851 + "id": "obj-keys-help", 852 + "maxclass": "comment", 853 + "numinlets": 1, 854 + "numoutlets": 0, 855 + "patching_rect": [ 856 + 870.0, 857 + 232.0, 858 + 80.0, 859 + 18.0 860 + ], 861 + "presentation": 0, 862 + "presentation_rect": [ 863 + 148.0, 864 + 166.0, 865 + 92.0, 866 + 16.0 867 + ], 868 + "text": "qwerty default", 869 + "fontname": "YWFTProcessing-Regular", 870 + "fontsize": 9.0, 871 + "textcolor": [ 872 + 0.47, 873 + 0.43, 874 + 0.57, 875 + 1.0 876 + ] 877 + } 878 + }, 879 + { 880 + "box": { 881 + "id": "obj-load-low", 882 + "maxclass": "newobj", 883 + "numinlets": 1, 884 + "numoutlets": 1, 885 + "outlettype": [ 886 + "int" 887 + ], 888 + "patching_rect": [ 889 + 760.0, 890 + 258.0, 891 + 65.0, 892 + 22.0 893 + ], 894 + "text": "loadmess 60" 895 + } 896 + }, 897 + { 898 + "box": { 899 + "id": "obj-load-high", 900 + "maxclass": "newobj", 901 + "numinlets": 1, 902 + "numoutlets": 1, 903 + "outlettype": [ 904 + "int" 905 + ], 906 + "patching_rect": [ 907 + 830.0, 908 + 258.0, 909 + 65.0, 910 + 22.0 911 + ], 912 + "text": "loadmess 76" 913 + } 914 + }, 915 + { 916 + "box": { 917 + "id": "obj-ui-range-pack", 918 + "maxclass": "newobj", 919 + "numinlets": 2, 920 + "numoutlets": 1, 921 + "outlettype": [ 922 + "" 923 + ], 924 + "patching_rect": [ 925 + 760.0, 926 + 286.0, 927 + 70.0, 928 + 22.0 929 + ], 930 + "text": "pak 60 76" 931 + } 932 + }, 933 + { 934 + "box": { 935 + "id": "obj-ui-range-msg", 936 + "maxclass": "message", 937 + "numinlets": 2, 938 + "numoutlets": 1, 939 + "outlettype": [ 940 + "" 941 + ], 942 + "patching_rect": [ 943 + 760.0, 944 + 314.0, 945 + 230.0, 946 + 22.0 947 + ], 948 + "text": "script if(window.acSpreadnobSetRange)window.acSpreadnobSetRange($1,$2)" 949 + } 950 + }, 951 + { 952 + "box": { 953 + "id": "obj-ui-note-msg", 954 + "maxclass": "message", 955 + "numinlets": 2, 956 + "numoutlets": 1, 957 + "outlettype": [ 958 + "" 959 + ], 960 + "patching_rect": [ 961 + 760.0, 962 + 342.0, 963 + 205.0, 964 + 22.0 965 + ], 966 + "text": "script if(window.acSpreadnobNote)window.acSpreadnobNote($1)" 967 + } 968 + }, 969 + { 970 + "box": { 971 + "id": "obj-ui-value-msg", 972 + "maxclass": "message", 973 + "numinlets": 2, 974 + "numoutlets": 1, 975 + "outlettype": [ 976 + "" 977 + ], 978 + "patching_rect": [ 979 + 760.0, 980 + 370.0, 981 + 215.0, 982 + 22.0 983 + ], 984 + "text": "script if(window.acSpreadnobSetValue)window.acSpreadnobSetValue($1)" 985 + } 986 + }, 987 + { 988 + "box": { 989 + "id": "obj-ui-active-msg", 990 + "maxclass": "message", 991 + "numinlets": 2, 992 + "numoutlets": 1, 993 + "outlettype": [ 994 + "" 995 + ], 996 + "patching_rect": [ 997 + 760.0, 998 + 398.0, 999 + 220.0, 1000 + 22.0 1001 + ], 1002 + "text": "script if(window.acSpreadnobSetActive)window.acSpreadnobSetActive($1)" 1003 + } 1004 + }, 1005 + { 1006 + "box": { 1007 + "id": "obj-target-name-symbol", 1008 + "maxclass": "newobj", 1009 + "numinlets": 2, 1010 + "numoutlets": 1, 1011 + "outlettype": [ 1012 + "" 1013 + ], 1014 + "patching_rect": [ 1015 + 760.0, 1016 + 426.0, 1017 + 60.0, 1018 + 22.0 1019 + ], 1020 + "text": "tosymbol" 1021 + } 1022 + }, 1023 + { 1024 + "box": { 1025 + "id": "obj-ui-target-msg", 1026 + "maxclass": "message", 1027 + "numinlets": 2, 1028 + "numoutlets": 1, 1029 + "outlettype": [ 1030 + "" 1031 + ], 1032 + "patching_rect": [ 1033 + 760.0, 1034 + 454.0, 1035 + 255.0, 1036 + 22.0 1037 + ], 1038 + "text": "script if(window.acSpreadnobSetTarget)window.acSpreadnobSetTarget(\"$1\")" 1039 + } 1040 + }, 1041 + { 1042 + "box": { 1043 + "id": "obj-device-path", 1044 + "maxclass": "newobj", 1045 + "numinlets": 1, 1046 + "numoutlets": 3, 1047 + "outlettype": [ 1048 + "", 1049 + "", 1050 + "" 1051 + ], 1052 + "patching_rect": [ 1053 + 30.0, 1054 + 470.0, 1055 + 125.0, 1056 + 22.0 1057 + ], 1058 + "text": "live.path this_device" 1059 + } 1060 + }, 1061 + { 1062 + "box": { 1063 + "id": "obj-device-observer", 1064 + "maxclass": "newobj", 1065 + "numinlets": 2, 1066 + "numoutlets": 3, 1067 + "outlettype": [ 1068 + "", 1069 + "", 1070 + "" 1071 + ], 1072 + "patching_rect": [ 1073 + 30.0, 1074 + 500.0, 1075 + 95.0, 1076 + 22.0 1077 + ], 1078 + "text": "live.observer" 1079 + } 1080 + }, 1081 + { 1082 + "box": { 1083 + "id": "obj-device-prop-active", 1084 + "maxclass": "message", 1085 + "numinlets": 2, 1086 + "numoutlets": 1, 1087 + "outlettype": [ 1088 + "" 1089 + ], 1090 + "patching_rect": [ 1091 + 135.0, 1092 + 500.0, 1093 + 95.0, 1094 + 22.0 1095 + ], 1096 + "text": "property is_active" 1097 + } 1098 + }, 264 1099 { 265 1100 "box": { 266 1101 "id": "obj-songpath", 267 1102 "maxclass": "newobj", 268 1103 "numinlets": 1, 269 1104 "numoutlets": 3, 270 - "outlettype": ["", "", ""], 271 - "patching_rect": [30.0, 70.0, 120.0, 22.0], 1105 + "outlettype": [ 1106 + "", 1107 + "", 1108 + "" 1109 + ], 1110 + "patching_rect": [ 1111 + 30.0, 1112 + 70.0, 1113 + 120.0, 1114 + 22.0 1115 + ], 272 1116 "text": "live.path" 273 1117 } 274 1118 }, ··· 276 1120 "box": { 277 1121 "id": "obj-grab-filter", 278 1122 "maxclass": "newobj", 279 - "numinlets": 2, 280 - "numoutlets": 2, 281 - "outlettype": ["bang", ""], 282 - "patching_rect": [200.0, 70.0, 30.0, 22.0], 283 - "text": "sel 1" 1123 + "numinlets": 1, 1124 + "numoutlets": 1, 1125 + "outlettype": [ 1126 + "bang" 1127 + ], 1128 + "patching_rect": [ 1129 + 200.0, 1130 + 70.0, 1131 + 30.0, 1132 + 22.0 1133 + ], 1134 + "text": "t b" 284 1135 } 285 1136 }, 286 1137 { ··· 289 1140 "maxclass": "message", 290 1141 "numinlets": 2, 291 1142 "numoutlets": 1, 292 - "outlettype": [""], 293 - "patching_rect": [200.0, 110.0, 165.0, 22.0], 1143 + "outlettype": [ 1144 + "" 1145 + ], 1146 + "patching_rect": [ 1147 + 200.0, 1148 + 110.0, 1149 + 165.0, 1150 + 22.0 1151 + ], 294 1152 "text": "path live_set view selected_parameter" 295 1153 } 296 1154 }, 297 1155 { 298 1156 "box": { 1157 + "id": "obj-grab-query", 1158 + "maxclass": "newobj", 1159 + "numinlets": 1, 1160 + "numoutlets": 3, 1161 + "outlettype": [ 1162 + "bang", 1163 + "bang", 1164 + "bang" 1165 + ], 1166 + "patching_rect": [ 1167 + 200.0, 1168 + 145.0, 1169 + 50.0, 1170 + 22.0 1171 + ], 1172 + "text": "t b b b" 1173 + } 1174 + }, 1175 + { 1176 + "box": { 1177 + "id": "obj-defer-getsel", 1178 + "maxclass": "newobj", 1179 + "numinlets": 1, 1180 + "numoutlets": 1, 1181 + "outlettype": [ 1182 + "bang" 1183 + ], 1184 + "patching_rect": [ 1185 + 260.0, 1186 + 145.0, 1187 + 55.0, 1188 + 22.0 1189 + ], 1190 + "text": "deferlow" 1191 + } 1192 + }, 1193 + { 1194 + "box": { 1195 + "id": "obj-songview-path", 1196 + "maxclass": "newobj", 1197 + "numinlets": 1, 1198 + "numoutlets": 3, 1199 + "outlettype": [ 1200 + "", 1201 + "", 1202 + "" 1203 + ], 1204 + "patching_rect": [ 1205 + 30.0, 1206 + 145.0, 1207 + 120.0, 1208 + 22.0 1209 + ], 1210 + "text": "live.path" 1211 + } 1212 + }, 1213 + { 1214 + "box": { 1215 + "id": "obj-msg-path-view", 1216 + "maxclass": "message", 1217 + "numinlets": 2, 1218 + "numoutlets": 1, 1219 + "outlettype": [ 1220 + "" 1221 + ], 1222 + "patching_rect": [ 1223 + 30.0, 1224 + 110.0, 1225 + 115.0, 1226 + 22.0 1227 + ], 1228 + "text": "path live_set view" 1229 + } 1230 + }, 1231 + { 1232 + "box": { 1233 + "id": "obj-songview", 1234 + "maxclass": "newobj", 1235 + "numinlets": 2, 1236 + "numoutlets": 3, 1237 + "outlettype": [ 1238 + "", 1239 + "", 1240 + "" 1241 + ], 1242 + "patching_rect": [ 1243 + 30.0, 1244 + 180.0, 1245 + 80.0, 1246 + 22.0 1247 + ], 1248 + "text": "live.object" 1249 + } 1250 + }, 1251 + { 1252 + "box": { 1253 + "id": "obj-msg-get-selected-parameter", 1254 + "maxclass": "message", 1255 + "numinlets": 2, 1256 + "numoutlets": 1, 1257 + "outlettype": [ 1258 + "" 1259 + ], 1260 + "patching_rect": [ 1261 + 120.0, 1262 + 180.0, 1263 + 145.0, 1264 + 22.0 1265 + ], 1266 + "text": "get selected_parameter" 1267 + } 1268 + }, 1269 + { 1270 + "box": { 1271 + "id": "obj-route-selected-parameter", 1272 + "maxclass": "newobj", 1273 + "numinlets": 2, 1274 + "numoutlets": 2, 1275 + "outlettype": [ 1276 + "", 1277 + "" 1278 + ], 1279 + "patching_rect": [ 1280 + 30.0, 1281 + 215.0, 1282 + 140.0, 1283 + 22.0 1284 + ], 1285 + "text": "route selected_parameter" 1286 + } 1287 + }, 1288 + { 1289 + "box": { 1290 + "id": "obj-route-target-id", 1291 + "maxclass": "newobj", 1292 + "numinlets": 2, 1293 + "numoutlets": 2, 1294 + "outlettype": [ 1295 + "", 1296 + "" 1297 + ], 1298 + "patching_rect": [ 1299 + 30.0, 1300 + 245.0, 1301 + 60.0, 1302 + 22.0 1303 + ], 1304 + "text": "route id" 1305 + } 1306 + }, 1307 + { 1308 + "box": { 1309 + "id": "obj-sel-target-zero", 1310 + "maxclass": "newobj", 1311 + "numinlets": 2, 1312 + "numoutlets": 2, 1313 + "outlettype": [ 1314 + "bang", 1315 + "" 1316 + ], 1317 + "patching_rect": [ 1318 + 100.0, 1319 + 245.0, 1320 + 40.0, 1321 + 22.0 1322 + ], 1323 + "text": "sel 0" 1324 + } 1325 + }, 1326 + { 1327 + "box": { 1328 + "id": "obj-target-trigger", 1329 + "maxclass": "newobj", 1330 + "numinlets": 1, 1331 + "numoutlets": 4, 1332 + "outlettype": [ 1333 + "bang", 1334 + "bang", 1335 + "bang", 1336 + "int" 1337 + ], 1338 + "patching_rect": [ 1339 + 150.0, 1340 + 245.0, 1341 + 65.0, 1342 + 22.0 1343 + ], 1344 + "text": "t b b b i" 1345 + } 1346 + }, 1347 + { 1348 + "box": { 1349 + "id": "obj-prepend-target-id", 1350 + "maxclass": "newobj", 1351 + "numinlets": 1, 1352 + "numoutlets": 1, 1353 + "outlettype": [ 1354 + "" 1355 + ], 1356 + "patching_rect": [ 1357 + 225.0, 1358 + 245.0, 1359 + 70.0, 1360 + 22.0 1361 + ], 1362 + "text": "prepend id" 1363 + } 1364 + }, 1365 + { 1366 + "box": { 299 1367 "id": "obj-reader-min", 300 1368 "maxclass": "newobj", 301 1369 "numinlets": 2, 302 1370 "numoutlets": 3, 303 - "outlettype": ["", "", ""], 304 - "patching_rect": [90.0, 270.0, 80.0, 22.0], 1371 + "outlettype": [ 1372 + "", 1373 + "", 1374 + "" 1375 + ], 1376 + "patching_rect": [ 1377 + 90.0, 1378 + 270.0, 1379 + 80.0, 1380 + 22.0 1381 + ], 305 1382 "text": "live.object" 306 1383 } 307 1384 }, 308 1385 { 309 1386 "box": { 1387 + "id": "obj-route-min", 1388 + "maxclass": "newobj", 1389 + "numinlets": 2, 1390 + "numoutlets": 2, 1391 + "outlettype": [ 1392 + "", 1393 + "" 1394 + ], 1395 + "patching_rect": [ 1396 + 90.0, 1397 + 300.0, 1398 + 65.0, 1399 + 22.0 1400 + ], 1401 + "text": "route min" 1402 + } 1403 + }, 1404 + { 1405 + "box": { 310 1406 "id": "obj-reader-max", 311 1407 "maxclass": "newobj", 312 1408 "numinlets": 2, 313 1409 "numoutlets": 3, 314 - "outlettype": ["", "", ""], 315 - "patching_rect": [230.0, 270.0, 80.0, 22.0], 1410 + "outlettype": [ 1411 + "", 1412 + "", 1413 + "" 1414 + ], 1415 + "patching_rect": [ 1416 + 230.0, 1417 + 270.0, 1418 + 80.0, 1419 + 22.0 1420 + ], 316 1421 "text": "live.object" 317 1422 } 318 1423 }, 319 1424 { 320 1425 "box": { 1426 + "id": "obj-route-max", 1427 + "maxclass": "newobj", 1428 + "numinlets": 2, 1429 + "numoutlets": 2, 1430 + "outlettype": [ 1431 + "", 1432 + "" 1433 + ], 1434 + "patching_rect": [ 1435 + 230.0, 1436 + 300.0, 1437 + 70.0, 1438 + 22.0 1439 + ], 1440 + "text": "route max" 1441 + } 1442 + }, 1443 + { 1444 + "box": { 321 1445 "id": "obj-reader-name", 322 1446 "maxclass": "newobj", 323 1447 "numinlets": 2, 324 1448 "numoutlets": 3, 325 - "outlettype": ["", "", ""], 326 - "patching_rect": [370.0, 270.0, 80.0, 22.0], 1449 + "outlettype": [ 1450 + "", 1451 + "", 1452 + "" 1453 + ], 1454 + "patching_rect": [ 1455 + 370.0, 1456 + 270.0, 1457 + 80.0, 1458 + 22.0 1459 + ], 327 1460 "text": "live.object" 328 1461 } 329 1462 }, 330 1463 { 331 1464 "box": { 1465 + "id": "obj-route-name", 1466 + "maxclass": "newobj", 1467 + "numinlets": 2, 1468 + "numoutlets": 2, 1469 + "outlettype": [ 1470 + "", 1471 + "" 1472 + ], 1473 + "patching_rect": [ 1474 + 370.0, 1475 + 300.0, 1476 + 75.0, 1477 + 22.0 1478 + ], 1479 + "text": "route name" 1480 + } 1481 + }, 1482 + { 1483 + "box": { 332 1484 "id": "obj-writer", 333 1485 "maxclass": "newobj", 334 1486 "numinlets": 2, 335 1487 "numoutlets": 3, 336 - "outlettype": ["", "", ""], 337 - "patching_rect": [500.0, 350.0, 80.0, 22.0], 1488 + "outlettype": [ 1489 + "", 1490 + "", 1491 + "" 1492 + ], 1493 + "patching_rect": [ 1494 + 500.0, 1495 + 350.0, 1496 + 80.0, 1497 + 22.0 1498 + ], 338 1499 "text": "live.object" 339 1500 } 340 1501 }, ··· 344 1505 "maxclass": "message", 345 1506 "numinlets": 2, 346 1507 "numoutlets": 1, 347 - "outlettype": [""], 348 - "patching_rect": [90.0, 240.0, 70.0, 22.0], 349 - "text": "getattr min" 1508 + "outlettype": [ 1509 + "" 1510 + ], 1511 + "patching_rect": [ 1512 + 90.0, 1513 + 240.0, 1514 + 70.0, 1515 + 22.0 1516 + ], 1517 + "text": "get min" 350 1518 } 351 1519 }, 352 1520 { ··· 355 1523 "maxclass": "message", 356 1524 "numinlets": 2, 357 1525 "numoutlets": 1, 358 - "outlettype": [""], 359 - "patching_rect": [230.0, 240.0, 75.0, 22.0], 360 - "text": "getattr max" 1526 + "outlettype": [ 1527 + "" 1528 + ], 1529 + "patching_rect": [ 1530 + 230.0, 1531 + 240.0, 1532 + 75.0, 1533 + 22.0 1534 + ], 1535 + "text": "get max" 361 1536 } 362 1537 }, 363 1538 { ··· 366 1541 "maxclass": "message", 367 1542 "numinlets": 2, 368 1543 "numoutlets": 1, 369 - "outlettype": [""], 370 - "patching_rect": [370.0, 240.0, 80.0, 22.0], 371 - "text": "getattr name" 1544 + "outlettype": [ 1545 + "" 1546 + ], 1547 + "patching_rect": [ 1548 + 370.0, 1549 + 240.0, 1550 + 80.0, 1551 + 22.0 1552 + ], 1553 + "text": "get name" 372 1554 } 373 1555 }, 374 1556 { ··· 377 1559 "maxclass": "message", 378 1560 "numinlets": 2, 379 1561 "numoutlets": 1, 380 - "outlettype": [""], 381 - "patching_rect": [500.0, 320.0, 85.0, 22.0], 1562 + "outlettype": [ 1563 + "" 1564 + ], 1565 + "patching_rect": [ 1566 + 500.0, 1567 + 320.0, 1568 + 85.0, 1569 + 22.0 1570 + ], 382 1571 "text": "set value $1" 383 1572 } 384 1573 }, ··· 388 1577 "maxclass": "newobj", 389 1578 "numinlets": 1, 390 1579 "numoutlets": 3, 391 - "outlettype": ["int", "int", "int"], 392 - "patching_rect": [500.0, 30.0, 50.0, 22.0], 1580 + "outlettype": [ 1581 + "int", 1582 + "int", 1583 + "int" 1584 + ], 1585 + "patching_rect": [ 1586 + 500.0, 1587 + 30.0, 1588 + 50.0, 1589 + 22.0 1590 + ], 393 1591 "text": "notein" 394 1592 } 395 1593 }, ··· 399 1597 "maxclass": "newobj", 400 1598 "numinlets": 2, 401 1599 "numoutlets": 2, 402 - "outlettype": ["int", "int"], 403 - "patching_rect": [500.0, 70.0, 60.0, 22.0], 1600 + "outlettype": [ 1601 + "int", 1602 + "int" 1603 + ], 1604 + "patching_rect": [ 1605 + 500.0, 1606 + 70.0, 1607 + 60.0, 1608 + 22.0 1609 + ], 404 1610 "text": "stripnote" 405 1611 } 406 1612 }, ··· 408 1614 "box": { 409 1615 "id": "obj-expr", 410 1616 "maxclass": "newobj", 1617 + "numinlets": 5, 1618 + "numoutlets": 1, 1619 + "outlettype": [ 1620 + "" 1621 + ], 1622 + "patching_rect": [ 1623 + 500.0, 1624 + 150.0, 1625 + 250.0, 1626 + 22.0 1627 + ], 1628 + "text": "expr (($f1 - $f4) / max(1., $f5 - $f4)) * ($f2 - $f3) + $f3" 1629 + } 1630 + }, 1631 + { 1632 + "box": { 1633 + "id": "obj-note-range-clip", 1634 + "maxclass": "newobj", 411 1635 "numinlets": 3, 412 1636 "numoutlets": 1, 413 - "outlettype": [""], 414 - "patching_rect": [500.0, 150.0, 250.0, 22.0], 415 - "text": "expr $f1 / 127. * ($f2 - $f3) + $f3" 1637 + "outlettype": [ 1638 + "" 1639 + ], 1640 + "patching_rect": [ 1641 + 500.0, 1642 + 130.0, 1643 + 90.0, 1644 + 22.0 1645 + ], 1646 + "text": "clip 60. 76." 416 1647 } 417 1648 }, 418 1649 { ··· 421 1652 "maxclass": "newobj", 422 1653 "numinlets": 3, 423 1654 "numoutlets": 3, 424 - "outlettype": ["bang", "bang", ""], 425 - "patching_rect": [430.0, 60.0, 50.0, 22.0], 1655 + "outlettype": [ 1656 + "bang", 1657 + "bang", 1658 + "" 1659 + ], 1660 + "patching_rect": [ 1661 + 430.0, 1662 + 60.0, 1663 + 50.0, 1664 + 22.0 1665 + ], 426 1666 "text": "sel 0 1" 427 1667 } 428 1668 }, ··· 432 1672 "maxclass": "newobj", 433 1673 "numinlets": 2, 434 1674 "numoutlets": 1, 435 - "outlettype": [""], 436 - "patching_rect": [500.0, 190.0, 85.0, 22.0], 1675 + "outlettype": [ 1676 + "" 1677 + ], 1678 + "patching_rect": [ 1679 + 500.0, 1680 + 190.0, 1681 + 85.0, 1682 + 22.0 1683 + ], 437 1684 "text": "gate 1 1" 438 1685 } 439 1686 }, ··· 443 1690 "maxclass": "newobj", 444 1691 "numinlets": 2, 445 1692 "numoutlets": 1, 446 - "outlettype": [""], 447 - "patching_rect": [500.0, 220.0, 85.0, 22.0], 1693 + "outlettype": [ 1694 + "" 1695 + ], 1696 + "patching_rect": [ 1697 + 500.0, 1698 + 220.0, 1699 + 85.0, 1700 + 22.0 1701 + ], 448 1702 "text": "gate 1 0" 449 1703 } 450 1704 }, ··· 454 1708 "maxclass": "newobj", 455 1709 "numinlets": 3, 456 1710 "numoutlets": 1, 457 - "outlettype": [""], 458 - "patching_rect": [500.0, 110.0, 80.0, 22.0], 1711 + "outlettype": [ 1712 + "" 1713 + ], 1714 + "patching_rect": [ 1715 + 500.0, 1716 + 110.0, 1717 + 80.0, 1718 + 22.0 1719 + ], 459 1720 "text": "clip 0. 127." 460 1721 } 461 1722 }, ··· 465 1726 "maxclass": "newobj", 466 1727 "numinlets": 1, 467 1728 "numoutlets": 1, 468 - "outlettype": ["float"], 469 - "patching_rect": [500.0, 250.0, 35.0, 22.0], 1729 + "outlettype": [ 1730 + "float" 1731 + ], 1732 + "patching_rect": [ 1733 + 500.0, 1734 + 250.0, 1735 + 35.0, 1736 + 22.0 1737 + ], 470 1738 "text": "t f" 471 1739 } 472 1740 }, ··· 476 1744 "maxclass": "newobj", 477 1745 "numinlets": 1, 478 1746 "numoutlets": 1, 479 - "outlettype": [""], 480 - "patching_rect": [370.0, 330.0, 85.0, 22.0], 1747 + "outlettype": [ 1748 + "" 1749 + ], 1750 + "patching_rect": [ 1751 + 370.0, 1752 + 330.0, 1753 + 85.0, 1754 + 22.0 1755 + ], 481 1756 "text": "prepend set" 482 1757 } 483 1758 }, ··· 487 1762 "maxclass": "newobj", 488 1763 "numinlets": 1, 489 1764 "numoutlets": 1, 490 - "outlettype": [""], 491 - "patching_rect": [600.0, 250.0, 85.0, 22.0], 1765 + "outlettype": [ 1766 + "" 1767 + ], 1768 + "patching_rect": [ 1769 + 600.0, 1770 + 250.0, 1771 + 85.0, 1772 + 22.0 1773 + ], 492 1774 "text": "prepend set" 493 1775 } 494 1776 }, ··· 498 1780 "maxclass": "newobj", 499 1781 "numinlets": 1, 500 1782 "numoutlets": 1, 501 - "outlettype": [""], 502 - "patching_rect": [600.0, 110.0, 85.0, 22.0], 1783 + "outlettype": [ 1784 + "" 1785 + ], 1786 + "patching_rect": [ 1787 + 600.0, 1788 + 110.0, 1789 + 85.0, 1790 + 22.0 1791 + ], 503 1792 "text": "prepend set" 504 1793 } 505 1794 }, ··· 509 1798 "maxclass": "newobj", 510 1799 "numinlets": 1, 511 1800 "numoutlets": 1, 512 - "outlettype": [""], 513 - "patching_rect": [90.0, 370.0, 85.0, 22.0], 1801 + "outlettype": [ 1802 + "" 1803 + ], 1804 + "patching_rect": [ 1805 + 90.0, 1806 + 370.0, 1807 + 85.0, 1808 + 22.0 1809 + ], 514 1810 "text": "prepend set" 515 1811 } 516 1812 }, ··· 520 1816 "maxclass": "newobj", 521 1817 "numinlets": 2, 522 1818 "numoutlets": 1, 523 - "outlettype": [""], 524 - "patching_rect": [90.0, 340.0, 140.0, 22.0], 525 - "text": "sprintf %g — %g" 1819 + "outlettype": [ 1820 + "" 1821 + ], 1822 + "patching_rect": [ 1823 + 90.0, 1824 + 340.0, 1825 + 140.0, 1826 + 22.0 1827 + ], 1828 + "text": "sprintf %g \u2014 %g" 526 1829 } 527 1830 }, 528 1831 { ··· 531 1834 "maxclass": "newobj", 532 1835 "numinlets": 2, 533 1836 "numoutlets": 1, 534 - "outlettype": ["float"], 535 - "patching_rect": [90.0, 310.0, 35.0, 22.0], 1837 + "outlettype": [ 1838 + "float" 1839 + ], 1840 + "patching_rect": [ 1841 + 90.0, 1842 + 310.0, 1843 + 35.0, 1844 + 22.0 1845 + ], 536 1846 "text": "float" 537 1847 } 538 1848 }, ··· 542 1852 "maxclass": "newobj", 543 1853 "numinlets": 2, 544 1854 "numoutlets": 1, 545 - "outlettype": ["float"], 546 - "patching_rect": [230.0, 310.0, 35.0, 22.0], 1855 + "outlettype": [ 1856 + "float" 1857 + ], 1858 + "patching_rect": [ 1859 + 230.0, 1860 + 310.0, 1861 + 35.0, 1862 + 22.0 1863 + ], 547 1864 "text": "float" 548 1865 } 549 1866 }, ··· 553 1870 "maxclass": "newobj", 554 1871 "numinlets": 1, 555 1872 "numoutlets": 1, 556 - "outlettype": ["int"], 557 - "patching_rect": [620.0, 30.0, 45.0, 22.0], 1873 + "outlettype": [ 1874 + "int" 1875 + ], 1876 + "patching_rect": [ 1877 + 620.0, 1878 + 30.0, 1879 + 45.0, 1880 + 22.0 1881 + ], 558 1882 "text": "midiin" 1883 + } 1884 + }, 1885 + { 1886 + "box": { 1887 + "id": "obj-midiparse", 1888 + "maxclass": "newobj", 1889 + "numinlets": 1, 1890 + "numoutlets": 8, 1891 + "outlettype": [ 1892 + "", 1893 + "", 1894 + "", 1895 + "int", 1896 + "int", 1897 + "int", 1898 + "int", 1899 + "" 1900 + ], 1901 + "patching_rect": [ 1902 + 680.0, 1903 + 30.0, 1904 + 70.0, 1905 + 22.0 1906 + ], 1907 + "text": "midiparse" 1908 + } 1909 + }, 1910 + { 1911 + "box": { 1912 + "id": "obj-unpack-note", 1913 + "maxclass": "newobj", 1914 + "numinlets": 1, 1915 + "numoutlets": 2, 1916 + "outlettype": [ 1917 + "int", 1918 + "int" 1919 + ], 1920 + "patching_rect": [ 1921 + 680.0, 1922 + 70.0, 1923 + 65.0, 1924 + 22.0 1925 + ], 1926 + "text": "unpack 0 0" 559 1927 } 560 1928 }, 561 1929 { ··· 564 1932 "maxclass": "newobj", 565 1933 "numinlets": 1, 566 1934 "numoutlets": 0, 567 - "patching_rect": [620.0, 70.0, 50.0, 22.0], 1935 + "patching_rect": [ 1936 + 620.0, 1937 + 70.0, 1938 + 50.0, 1939 + 22.0 1940 + ], 568 1941 "text": "midiout" 569 1942 } 570 1943 }, ··· 574 1947 "maxclass": "newobj", 575 1948 "numinlets": 2, 576 1949 "numoutlets": 1, 577 - "outlettype": [""], 578 - "patching_rect": [500.0, 285.0, 55.0, 22.0], 1950 + "outlettype": [ 1951 + "" 1952 + ], 1953 + "patching_rect": [ 1954 + 500.0, 1955 + 285.0, 1956 + 55.0, 1957 + 22.0 1958 + ], 579 1959 "text": "gate 1 0" 580 1960 } 581 1961 }, ··· 585 1965 "maxclass": "message", 586 1966 "numinlets": 2, 587 1967 "numoutlets": 1, 588 - "outlettype": [""], 589 - "patching_rect": [440.0, 330.0, 18.0, 22.0], 1968 + "outlettype": [ 1969 + "" 1970 + ], 1971 + "patching_rect": [ 1972 + 440.0, 1973 + 330.0, 1974 + 18.0, 1975 + 22.0 1976 + ], 590 1977 "text": "1" 591 1978 } 592 1979 }, ··· 596 1983 "maxclass": "newobj", 597 1984 "numinlets": 1, 598 1985 "numoutlets": 0, 599 - "patching_rect": [30.0, 420.0, 115.0, 22.0], 1986 + "patching_rect": [ 1987 + 30.0, 1988 + 420.0, 1989 + 115.0, 1990 + 22.0 1991 + ], 600 1992 "text": "print [AC-SPREADNOB]" 601 1993 } 1994 + }, 1995 + { 1996 + "box": { 1997 + "id": "obj-print-grab", 1998 + "maxclass": "newobj", 1999 + "numinlets": 1, 2000 + "numoutlets": 0, 2001 + "patching_rect": [ 2002 + 160.0, 2003 + 420.0, 2004 + 140.0, 2005 + 22.0 2006 + ], 2007 + "text": "print [AC-SPREADNOB/GRAB]" 2008 + } 2009 + }, 2010 + { 2011 + "box": { 2012 + "id": "obj-print-getsel", 2013 + "maxclass": "newobj", 2014 + "numinlets": 1, 2015 + "numoutlets": 0, 2016 + "patching_rect": [ 2017 + 310.0, 2018 + 420.0, 2019 + 155.0, 2020 + 22.0 2021 + ], 2022 + "text": "print [AC-SPREADNOB/GETSEL]" 2023 + } 2024 + }, 2025 + { 2026 + "box": { 2027 + "id": "obj-print-path-msg", 2028 + "maxclass": "newobj", 2029 + "numinlets": 1, 2030 + "numoutlets": 0, 2031 + "patching_rect": [ 2032 + 30.0, 2033 + 450.0, 2034 + 165.0, 2035 + 22.0 2036 + ], 2037 + "text": "print [AC-SPREADNOB/PATH-MSG]" 2038 + } 2039 + }, 2040 + { 2041 + "box": { 2042 + "id": "obj-print-path-id", 2043 + "maxclass": "newobj", 2044 + "numinlets": 1, 2045 + "numoutlets": 0, 2046 + "patching_rect": [ 2047 + 205.0, 2048 + 450.0, 2049 + 155.0, 2050 + 22.0 2051 + ], 2052 + "text": "print [AC-SPREADNOB/PATH-ID]" 2053 + } 2054 + }, 2055 + { 2056 + "box": { 2057 + "id": "obj-print-view-id", 2058 + "maxclass": "newobj", 2059 + "numinlets": 1, 2060 + "numoutlets": 0, 2061 + "patching_rect": [ 2062 + 370.0, 2063 + 420.0, 2064 + 155.0, 2065 + 22.0 2066 + ], 2067 + "text": "print [AC-SPREADNOB/VIEW-ID]" 2068 + } 2069 + }, 2070 + { 2071 + "box": { 2072 + "id": "obj-print-path-mid", 2073 + "maxclass": "newobj", 2074 + "numinlets": 1, 2075 + "numoutlets": 0, 2076 + "patching_rect": [ 2077 + 535.0, 2078 + 420.0, 2079 + 165.0, 2080 + 22.0 2081 + ], 2082 + "text": "print [AC-SPREADNOB/PATH-MID]" 2083 + } 2084 + }, 2085 + { 2086 + "box": { 2087 + "id": "obj-print-view-out", 2088 + "maxclass": "newobj", 2089 + "numinlets": 1, 2090 + "numoutlets": 0, 2091 + "patching_rect": [ 2092 + 710.0, 2093 + 420.0, 2094 + 160.0, 2095 + 22.0 2096 + ], 2097 + "text": "print [AC-SPREADNOB/VIEW-OUT]" 2098 + } 2099 + }, 2100 + { 2101 + "box": { 2102 + "id": "obj-print-selected-parameter", 2103 + "maxclass": "newobj", 2104 + "numinlets": 1, 2105 + "numoutlets": 0, 2106 + "patching_rect": [ 2107 + 710.0, 2108 + 450.0, 2109 + 195.0, 2110 + 22.0 2111 + ], 2112 + "text": "print [AC-SPREADNOB/SELECTED-PARAM]" 2113 + } 2114 + }, 2115 + { 2116 + "box": { 2117 + "id": "obj-print-target-id", 2118 + "maxclass": "newobj", 2119 + "numinlets": 1, 2120 + "numoutlets": 0, 2121 + "patching_rect": [ 2122 + 710.0, 2123 + 480.0, 2124 + 175.0, 2125 + 22.0 2126 + ], 2127 + "text": "print [AC-SPREADNOB/TARGET-ID]" 2128 + } 2129 + }, 2130 + { 2131 + "box": { 2132 + "id": "obj-print-target-zero", 2133 + "maxclass": "newobj", 2134 + "numinlets": 1, 2135 + "numoutlets": 0, 2136 + "patching_rect": [ 2137 + 710.0, 2138 + 510.0, 2139 + 185.0, 2140 + 22.0 2141 + ], 2142 + "text": "print [AC-SPREADNOB/TARGET-ID-0]" 2143 + } 2144 + }, 2145 + { 2146 + "box": { 2147 + "id": "obj-print-min", 2148 + "maxclass": "newobj", 2149 + "numinlets": 1, 2150 + "numoutlets": 0, 2151 + "patching_rect": [ 2152 + 370.0, 2153 + 450.0, 2154 + 140.0, 2155 + 22.0 2156 + ], 2157 + "text": "print [AC-SPREADNOB/MIN]" 2158 + } 2159 + }, 2160 + { 2161 + "box": { 2162 + "id": "obj-print-max", 2163 + "maxclass": "newobj", 2164 + "numinlets": 1, 2165 + "numoutlets": 0, 2166 + "patching_rect": [ 2167 + 520.0, 2168 + 450.0, 2169 + 140.0, 2170 + 22.0 2171 + ], 2172 + "text": "print [AC-SPREADNOB/MAX]" 2173 + } 2174 + }, 2175 + { 2176 + "box": { 2177 + "id": "obj-print-name", 2178 + "maxclass": "newobj", 2179 + "numinlets": 1, 2180 + "numoutlets": 0, 2181 + "patching_rect": [ 2182 + 670.0, 2183 + 450.0, 2184 + 150.0, 2185 + 22.0 2186 + ], 2187 + "text": "print [AC-SPREADNOB/NAME]" 2188 + } 2189 + }, 2190 + { 2191 + "box": { 2192 + "id": "obj-print-mode", 2193 + "maxclass": "newobj", 2194 + "numinlets": 1, 2195 + "numoutlets": 0, 2196 + "patching_rect": [ 2197 + 30.0, 2198 + 480.0, 2199 + 150.0, 2200 + 22.0 2201 + ], 2202 + "text": "print [AC-SPREADNOB/MODE]" 2203 + } 2204 + }, 2205 + { 2206 + "box": { 2207 + "id": "obj-print-note", 2208 + "maxclass": "newobj", 2209 + "numinlets": 1, 2210 + "numoutlets": 0, 2211 + "patching_rect": [ 2212 + 190.0, 2213 + 480.0, 2214 + 150.0, 2215 + 22.0 2216 + ], 2217 + "text": "print [AC-SPREADNOB/NOTE]" 2218 + } 2219 + }, 2220 + { 2221 + "box": { 2222 + "id": "obj-print-velocity", 2223 + "maxclass": "newobj", 2224 + "numinlets": 1, 2225 + "numoutlets": 0, 2226 + "patching_rect": [ 2227 + 350.0, 2228 + 480.0, 2229 + 175.0, 2230 + 22.0 2231 + ], 2232 + "text": "print [AC-SPREADNOB/VELOCITY]" 2233 + } 2234 + }, 2235 + { 2236 + "box": { 2237 + "id": "obj-print-channel", 2238 + "maxclass": "newobj", 2239 + "numinlets": 1, 2240 + "numoutlets": 0, 2241 + "patching_rect": [ 2242 + 535.0, 2243 + 480.0, 2244 + 165.0, 2245 + 22.0 2246 + ], 2247 + "text": "print [AC-SPREADNOB/CHANNEL]" 2248 + } 2249 + }, 2250 + { 2251 + "box": { 2252 + "id": "obj-print-stripnote-note", 2253 + "maxclass": "newobj", 2254 + "numinlets": 1, 2255 + "numoutlets": 0, 2256 + "patching_rect": [ 2257 + 30.0, 2258 + 510.0, 2259 + 215.0, 2260 + 22.0 2261 + ], 2262 + "text": "print [AC-SPREADNOB/STRIPNOTE-NOTE]" 2263 + } 2264 + }, 2265 + { 2266 + "box": { 2267 + "id": "obj-print-stripnote-velocity", 2268 + "maxclass": "newobj", 2269 + "numinlets": 1, 2270 + "numoutlets": 0, 2271 + "patching_rect": [ 2272 + 255.0, 2273 + 510.0, 2274 + 235.0, 2275 + 22.0 2276 + ], 2277 + "text": "print [AC-SPREADNOB/STRIPNOTE-VELOCITY]" 2278 + } 2279 + }, 2280 + { 2281 + "box": { 2282 + "id": "obj-print-scaled", 2283 + "maxclass": "newobj", 2284 + "numinlets": 1, 2285 + "numoutlets": 0, 2286 + "patching_rect": [ 2287 + 500.0, 2288 + 510.0, 2289 + 160.0, 2290 + 22.0 2291 + ], 2292 + "text": "print [AC-SPREADNOB/SCALED]" 2293 + } 2294 + }, 2295 + { 2296 + "box": { 2297 + "id": "obj-print-direct", 2298 + "maxclass": "newobj", 2299 + "numinlets": 1, 2300 + "numoutlets": 0, 2301 + "patching_rect": [ 2302 + 670.0, 2303 + 510.0, 2304 + 160.0, 2305 + 22.0 2306 + ], 2307 + "text": "print [AC-SPREADNOB/DIRECT]" 2308 + } 2309 + }, 2310 + { 2311 + "box": { 2312 + "id": "obj-print-write", 2313 + "maxclass": "newobj", 2314 + "numinlets": 1, 2315 + "numoutlets": 0, 2316 + "patching_rect": [ 2317 + 30.0, 2318 + 540.0, 2319 + 155.0, 2320 + 22.0 2321 + ], 2322 + "text": "print [AC-SPREADNOB/WRITE]" 2323 + } 2324 + }, 2325 + { 2326 + "box": { 2327 + "id": "obj-print-setval", 2328 + "maxclass": "newobj", 2329 + "numinlets": 1, 2330 + "numoutlets": 0, 2331 + "patching_rect": [ 2332 + 195.0, 2333 + 540.0, 2334 + 165.0, 2335 + 22.0 2336 + ], 2337 + "text": "print [AC-SPREADNOB/SETVAL]" 2338 + } 2339 + }, 2340 + { 2341 + "box": { 2342 + "id": "obj-print-writer", 2343 + "maxclass": "newobj", 2344 + "numinlets": 1, 2345 + "numoutlets": 0, 2346 + "patching_rect": [ 2347 + 370.0, 2348 + 540.0, 2349 + 165.0, 2350 + 22.0 2351 + ], 2352 + "text": "print [AC-SPREADNOB/WRITER]" 2353 + } 2354 + }, 2355 + { 2356 + "box": { 2357 + "id": "obj-print-midi", 2358 + "maxclass": "newobj", 2359 + "numinlets": 1, 2360 + "numoutlets": 0, 2361 + "patching_rect": [ 2362 + 545.0, 2363 + 540.0, 2364 + 150.0, 2365 + 22.0 2366 + ], 2367 + "text": "print [AC-SPREADNOB/MIDI]" 2368 + } 2369 + }, 2370 + { 2371 + "box": { 2372 + "id": "obj-sprintf-midi-raw", 2373 + "maxclass": "newobj", 2374 + "numinlets": 1, 2375 + "numoutlets": 1, 2376 + "outlettype": [ 2377 + "" 2378 + ], 2379 + "patching_rect": [ 2380 + 705.0, 2381 + 540.0, 2382 + 95.0, 2383 + 22.0 2384 + ], 2385 + "text": "sprintf raw %ld" 2386 + } 2387 + }, 2388 + { 2389 + "box": { 2390 + "id": "obj-sprintf-midi-note", 2391 + "maxclass": "newobj", 2392 + "numinlets": 2, 2393 + "numoutlets": 1, 2394 + "outlettype": [ 2395 + "" 2396 + ], 2397 + "patching_rect": [ 2398 + 705.0, 2399 + 570.0, 2400 + 120.0, 2401 + 22.0 2402 + ], 2403 + "text": "sprintf n%ld v%ld" 2404 + } 2405 + }, 2406 + { 2407 + "box": { 2408 + "id": "obj-prepend-set-midi-display", 2409 + "maxclass": "newobj", 2410 + "numinlets": 1, 2411 + "numoutlets": 1, 2412 + "outlettype": [ 2413 + "" 2414 + ], 2415 + "patching_rect": [ 2416 + 705.0, 2417 + 600.0, 2418 + 75.0, 2419 + 22.0 2420 + ], 2421 + "text": "prepend set" 2422 + } 602 2423 } 603 2424 ], 604 - 605 2425 "lines": [ 606 - {"patchline": {"destination": ["obj-grab-filter", 0], "source": ["obj-grab", 0]}}, 607 - {"patchline": {"destination": ["obj-msg-getsel", 0], "source": ["obj-grab-filter", 0]}}, 608 - {"patchline": {"destination": ["obj-songpath", 0], "source": ["obj-msg-getsel", 0]}}, 609 - 610 - {"patchline": {"destination": ["obj-reader-min", 1], "source": ["obj-songpath", 1]}}, 611 - {"patchline": {"destination": ["obj-reader-max", 1], "source": ["obj-songpath", 1]}}, 612 - {"patchline": {"destination": ["obj-reader-name", 1], "source": ["obj-songpath", 1]}}, 613 - {"patchline": {"destination": ["obj-writer", 1], "source": ["obj-songpath", 1]}}, 614 - 615 - {"patchline": {"destination": ["obj-msg-getmin", 0], "source": ["obj-songpath", 0]}}, 616 - {"patchline": {"destination": ["obj-msg-getmax", 0], "source": ["obj-songpath", 0]}}, 617 - {"patchline": {"destination": ["obj-msg-getname", 0], "source": ["obj-songpath", 0]}}, 618 - 619 - {"patchline": {"destination": ["obj-reader-min", 0], "source": ["obj-msg-getmin", 0]}}, 620 - {"patchline": {"destination": ["obj-reader-max", 0], "source": ["obj-msg-getmax", 0]}}, 621 - {"patchline": {"destination": ["obj-reader-name", 0], "source": ["obj-msg-getname", 0]}}, 622 - 623 - {"patchline": {"destination": ["obj-store-min", 0], "source": ["obj-reader-min", 0]}}, 624 - {"patchline": {"destination": ["obj-store-max", 0], "source": ["obj-reader-max", 0]}}, 625 - 626 - {"patchline": {"destination": ["obj-expr", 2], "source": ["obj-store-min", 0]}}, 627 - {"patchline": {"destination": ["obj-expr", 1], "source": ["obj-store-max", 0]}}, 628 - 629 - {"patchline": {"destination": ["obj-clip", 1], "source": ["obj-store-min", 0]}}, 630 - {"patchline": {"destination": ["obj-clip", 2], "source": ["obj-store-max", 0]}}, 631 - 632 - {"patchline": {"destination": ["obj-sprintf-range", 0], "source": ["obj-store-min", 0]}}, 633 - {"patchline": {"destination": ["obj-sprintf-range", 1], "source": ["obj-store-max", 0]}}, 634 - {"patchline": {"destination": ["obj-prepend-set-range", 0], "source": ["obj-sprintf-range", 0]}}, 635 - {"patchline": {"destination": ["obj-range-display", 0], "source": ["obj-prepend-set-range", 0]}}, 636 - 637 - {"patchline": {"destination": ["obj-prepend-set-status", 0], "source": ["obj-reader-name", 0]}}, 638 - {"patchline": {"destination": ["obj-status", 0], "source": ["obj-prepend-set-status", 0]}}, 639 - {"patchline": {"destination": ["obj-print", 0], "source": ["obj-reader-name", 0]}}, 640 - 641 - {"patchline": {"destination": ["obj-stripnote", 0], "source": ["obj-notein", 0]}}, 642 - {"patchline": {"destination": ["obj-stripnote", 1], "source": ["obj-notein", 1]}}, 643 - 644 - {"patchline": {"destination": ["obj-expr", 0], "source": ["obj-stripnote", 0]}}, 645 - {"patchline": {"destination": ["obj-clip", 0], "source": ["obj-stripnote", 0]}}, 646 - 647 - {"patchline": {"destination": ["obj-prepend-set-note", 0], "source": ["obj-stripnote", 0]}}, 648 - {"patchline": {"destination": ["obj-note-display", 0], "source": ["obj-prepend-set-note", 0]}}, 649 - 650 - {"patchline": {"destination": ["obj-gate-scaled", 1], "source": ["obj-expr", 0]}}, 651 - {"patchline": {"destination": ["obj-gate-direct", 1], "source": ["obj-clip", 0]}}, 652 - 653 - {"patchline": {"destination": ["obj-tofloat", 0], "source": ["obj-gate-scaled", 0]}}, 654 - {"patchline": {"destination": ["obj-tofloat", 0], "source": ["obj-gate-direct", 0]}}, 655 - 656 - {"patchline": {"destination": ["obj-master-gate", 1], "source": ["obj-tofloat", 0]}}, 657 - {"patchline": {"destination": ["obj-msg-setval", 0], "source": ["obj-master-gate", 0]}}, 658 - {"patchline": {"destination": ["obj-writer", 0], "source": ["obj-msg-setval", 0]}}, 659 - 660 - {"patchline": {"destination": ["obj-prepend-set-val", 0], "source": ["obj-master-gate", 0]}}, 661 - {"patchline": {"destination": ["obj-val-display", 0], "source": ["obj-prepend-set-val", 0]}}, 662 - 663 - {"patchline": {"destination": ["obj-open-gate", 0], "source": ["obj-reader-name", 0]}}, 664 - {"patchline": {"destination": ["obj-master-gate", 0], "source": ["obj-open-gate", 0]}}, 665 - 666 - {"patchline": {"destination": ["obj-gate-scaled", 0], "source": ["obj-mode-sel", 0]}}, 667 - {"patchline": {"destination": ["obj-gate-direct", 0], "source": ["obj-mode-sel", 1]}}, 668 - {"patchline": {"destination": ["obj-mode-sel", 0], "source": ["obj-mode", 0]}}, 669 - 670 - {"patchline": {"destination": ["obj-midiout", 0], "source": ["obj-midiin", 0]}} 2426 + { 2427 + "patchline": { 2428 + "destination": [ 2429 + "obj-jweb-route", 2430 + 0 2431 + ], 2432 + "source": [ 2433 + "obj-jweb", 2434 + 2 2435 + ] 2436 + } 2437 + }, 2438 + { 2439 + "patchline": { 2440 + "destination": [ 2441 + "obj-jweb-print", 2442 + 0 2443 + ], 2444 + "source": [ 2445 + "obj-jweb", 2446 + 2 2447 + ] 2448 + } 2449 + }, 2450 + { 2451 + "patchline": { 2452 + "destination": [ 2453 + "obj-jweb-activate", 2454 + 0 2455 + ], 2456 + "source": [ 2457 + "obj-jweb-route", 2458 + 0 2459 + ] 2460 + } 2461 + }, 2462 + { 2463 + "patchline": { 2464 + "destination": [ 2465 + "obj-jweb", 2466 + 0 2467 + ], 2468 + "source": [ 2469 + "obj-jweb-activate", 2470 + 0 2471 + ] 2472 + } 2473 + }, 2474 + { 2475 + "patchline": { 2476 + "destination": [ 2477 + "obj-jweb-range", 2478 + 0 2479 + ], 2480 + "source": [ 2481 + "obj-jweb-route", 2482 + 1 2483 + ] 2484 + } 2485 + }, 2486 + { 2487 + "patchline": { 2488 + "destination": [ 2489 + "obj-midi-low", 2490 + 0 2491 + ], 2492 + "source": [ 2493 + "obj-jweb-range", 2494 + 0 2495 + ] 2496 + } 2497 + }, 2498 + { 2499 + "patchline": { 2500 + "destination": [ 2501 + "obj-midi-high", 2502 + 0 2503 + ], 2504 + "source": [ 2505 + "obj-jweb-range", 2506 + 1 2507 + ] 2508 + } 2509 + }, 2510 + { 2511 + "patchline": { 2512 + "destination": [ 2513 + "obj-midi-low", 2514 + 0 2515 + ], 2516 + "source": [ 2517 + "obj-load-low", 2518 + 0 2519 + ] 2520 + } 2521 + }, 2522 + { 2523 + "patchline": { 2524 + "destination": [ 2525 + "obj-midi-high", 2526 + 0 2527 + ], 2528 + "source": [ 2529 + "obj-load-high", 2530 + 0 2531 + ] 2532 + } 2533 + }, 2534 + { 2535 + "patchline": { 2536 + "destination": [ 2537 + "obj-midi-low", 2538 + 0 2539 + ], 2540 + "source": [ 2541 + "obj-jweb-route", 2542 + 0 2543 + ] 2544 + } 2545 + }, 2546 + { 2547 + "patchline": { 2548 + "destination": [ 2549 + "obj-midi-high", 2550 + 0 2551 + ], 2552 + "source": [ 2553 + "obj-jweb-route", 2554 + 0 2555 + ] 2556 + } 2557 + }, 2558 + { 2559 + "patchline": { 2560 + "destination": [ 2561 + "obj-ui-range-pack", 2562 + 0 2563 + ], 2564 + "source": [ 2565 + "obj-midi-low", 2566 + 0 2567 + ] 2568 + } 2569 + }, 2570 + { 2571 + "patchline": { 2572 + "destination": [ 2573 + "obj-ui-range-pack", 2574 + 1 2575 + ], 2576 + "source": [ 2577 + "obj-midi-high", 2578 + 0 2579 + ] 2580 + } 2581 + }, 2582 + { 2583 + "patchline": { 2584 + "destination": [ 2585 + "obj-ui-range-msg", 2586 + 0 2587 + ], 2588 + "source": [ 2589 + "obj-ui-range-pack", 2590 + 0 2591 + ] 2592 + } 2593 + }, 2594 + { 2595 + "patchline": { 2596 + "destination": [ 2597 + "obj-jweb", 2598 + 0 2599 + ], 2600 + "source": [ 2601 + "obj-ui-range-msg", 2602 + 0 2603 + ] 2604 + } 2605 + }, 2606 + { 2607 + "patchline": { 2608 + "destination": [ 2609 + "obj-expr", 2610 + 3 2611 + ], 2612 + "source": [ 2613 + "obj-midi-low", 2614 + 0 2615 + ] 2616 + } 2617 + }, 2618 + { 2619 + "patchline": { 2620 + "destination": [ 2621 + "obj-expr", 2622 + 4 2623 + ], 2624 + "source": [ 2625 + "obj-midi-high", 2626 + 0 2627 + ] 2628 + } 2629 + }, 2630 + { 2631 + "patchline": { 2632 + "destination": [ 2633 + "obj-note-range-clip", 2634 + 1 2635 + ], 2636 + "source": [ 2637 + "obj-midi-low", 2638 + 0 2639 + ] 2640 + } 2641 + }, 2642 + { 2643 + "patchline": { 2644 + "destination": [ 2645 + "obj-note-range-clip", 2646 + 2 2647 + ], 2648 + "source": [ 2649 + "obj-midi-high", 2650 + 0 2651 + ] 2652 + } 2653 + }, 2654 + { 2655 + "patchline": { 2656 + "destination": [ 2657 + "obj-device-path", 2658 + 0 2659 + ], 2660 + "source": [ 2661 + "obj-thisdevice", 2662 + 0 2663 + ] 2664 + } 2665 + }, 2666 + { 2667 + "patchline": { 2668 + "destination": [ 2669 + "obj-device-prop-active", 2670 + 0 2671 + ], 2672 + "source": [ 2673 + "obj-device-path", 2674 + 0 2675 + ] 2676 + } 2677 + }, 2678 + { 2679 + "patchline": { 2680 + "destination": [ 2681 + "obj-device-observer", 2682 + 0 2683 + ], 2684 + "source": [ 2685 + "obj-device-prop-active", 2686 + 0 2687 + ] 2688 + } 2689 + }, 2690 + { 2691 + "patchline": { 2692 + "destination": [ 2693 + "obj-ui-active-msg", 2694 + 0 2695 + ], 2696 + "source": [ 2697 + "obj-device-observer", 2698 + 1 2699 + ] 2700 + } 2701 + }, 2702 + { 2703 + "patchline": { 2704 + "destination": [ 2705 + "obj-jweb", 2706 + 0 2707 + ], 2708 + "source": [ 2709 + "obj-ui-active-msg", 2710 + 0 2711 + ] 2712 + } 2713 + }, 2714 + { 2715 + "patchline": { 2716 + "destination": [ 2717 + "obj-msg-getsel", 2718 + 0 2719 + ], 2720 + "source": [ 2721 + "obj-thisdevice", 2722 + 0 2723 + ] 2724 + } 2725 + }, 2726 + { 2727 + "patchline": { 2728 + "destination": [ 2729 + "obj-msg-path-view", 2730 + 0 2731 + ], 2732 + "source": [ 2733 + "obj-thisdevice", 2734 + 0 2735 + ] 2736 + } 2737 + }, 2738 + { 2739 + "patchline": { 2740 + "destination": [ 2741 + "obj-grab-filter", 2742 + 0 2743 + ], 2744 + "source": [ 2745 + "obj-grab", 2746 + 0 2747 + ] 2748 + } 2749 + }, 2750 + { 2751 + "patchline": { 2752 + "destination": [ 2753 + "obj-grab-query", 2754 + 0 2755 + ], 2756 + "source": [ 2757 + "obj-grab-filter", 2758 + 0 2759 + ] 2760 + } 2761 + }, 2762 + { 2763 + "patchline": { 2764 + "destination": [ 2765 + "obj-print-grab", 2766 + 0 2767 + ], 2768 + "source": [ 2769 + "obj-grab", 2770 + 0 2771 + ] 2772 + } 2773 + }, 2774 + { 2775 + "patchline": { 2776 + "destination": [ 2777 + "obj-defer-getsel", 2778 + 0 2779 + ], 2780 + "source": [ 2781 + "obj-grab-query", 2782 + 0 2783 + ] 2784 + } 2785 + }, 2786 + { 2787 + "patchline": { 2788 + "destination": [ 2789 + "obj-msg-path-view", 2790 + 0 2791 + ], 2792 + "source": [ 2793 + "obj-grab-query", 2794 + 1 2795 + ] 2796 + } 2797 + }, 2798 + { 2799 + "patchline": { 2800 + "destination": [ 2801 + "obj-msg-getsel", 2802 + 0 2803 + ], 2804 + "source": [ 2805 + "obj-grab-query", 2806 + 2 2807 + ] 2808 + } 2809 + }, 2810 + { 2811 + "patchline": { 2812 + "destination": [ 2813 + "obj-msg-getsel", 2814 + 0 2815 + ], 2816 + "source": [ 2817 + "obj-defer-getsel", 2818 + 0 2819 + ] 2820 + } 2821 + }, 2822 + { 2823 + "patchline": { 2824 + "destination": [ 2825 + "obj-print-getsel", 2826 + 0 2827 + ], 2828 + "source": [ 2829 + "obj-msg-getsel", 2830 + 0 2831 + ] 2832 + } 2833 + }, 2834 + { 2835 + "patchline": { 2836 + "destination": [ 2837 + "obj-songpath", 2838 + 0 2839 + ], 2840 + "source": [ 2841 + "obj-msg-getsel", 2842 + 0 2843 + ] 2844 + } 2845 + }, 2846 + { 2847 + "patchline": { 2848 + "destination": [ 2849 + "obj-songview-path", 2850 + 0 2851 + ], 2852 + "source": [ 2853 + "obj-msg-path-view", 2854 + 0 2855 + ] 2856 + } 2857 + }, 2858 + { 2859 + "patchline": { 2860 + "destination": [ 2861 + "obj-print-view-id", 2862 + 0 2863 + ], 2864 + "source": [ 2865 + "obj-songview-path", 2866 + 0 2867 + ] 2868 + } 2869 + }, 2870 + { 2871 + "patchline": { 2872 + "destination": [ 2873 + "obj-songview", 2874 + 1 2875 + ], 2876 + "source": [ 2877 + "obj-songview-path", 2878 + 0 2879 + ] 2880 + } 2881 + }, 2882 + { 2883 + "patchline": { 2884 + "destination": [ 2885 + "obj-print-path-mid", 2886 + 0 2887 + ], 2888 + "source": [ 2889 + "obj-songview-path", 2890 + 1 2891 + ] 2892 + } 2893 + }, 2894 + { 2895 + "patchline": { 2896 + "destination": [ 2897 + "obj-msg-get-selected-parameter", 2898 + 0 2899 + ], 2900 + "source": [ 2901 + "obj-defer-getsel", 2902 + 0 2903 + ] 2904 + } 2905 + }, 2906 + { 2907 + "patchline": { 2908 + "destination": [ 2909 + "obj-songview", 2910 + 0 2911 + ], 2912 + "source": [ 2913 + "obj-msg-get-selected-parameter", 2914 + 0 2915 + ] 2916 + } 2917 + }, 2918 + { 2919 + "patchline": { 2920 + "destination": [ 2921 + "obj-print-view-out", 2922 + 0 2923 + ], 2924 + "source": [ 2925 + "obj-songview", 2926 + 0 2927 + ] 2928 + } 2929 + }, 2930 + { 2931 + "patchline": { 2932 + "destination": [ 2933 + "obj-route-selected-parameter", 2934 + 0 2935 + ], 2936 + "source": [ 2937 + "obj-songview", 2938 + 0 2939 + ] 2940 + } 2941 + }, 2942 + { 2943 + "patchline": { 2944 + "destination": [ 2945 + "obj-print-selected-parameter", 2946 + 0 2947 + ], 2948 + "source": [ 2949 + "obj-route-selected-parameter", 2950 + 0 2951 + ] 2952 + } 2953 + }, 2954 + { 2955 + "patchline": { 2956 + "destination": [ 2957 + "obj-route-target-id", 2958 + 0 2959 + ], 2960 + "source": [ 2961 + "obj-route-selected-parameter", 2962 + 0 2963 + ] 2964 + } 2965 + }, 2966 + { 2967 + "patchline": { 2968 + "destination": [ 2969 + "obj-sel-target-zero", 2970 + 0 2971 + ], 2972 + "source": [ 2973 + "obj-route-target-id", 2974 + 0 2975 + ] 2976 + } 2977 + }, 2978 + { 2979 + "patchline": { 2980 + "destination": [ 2981 + "obj-print-target-zero", 2982 + 0 2983 + ], 2984 + "source": [ 2985 + "obj-sel-target-zero", 2986 + 0 2987 + ] 2988 + } 2989 + }, 2990 + { 2991 + "patchline": { 2992 + "destination": [ 2993 + "obj-target-trigger", 2994 + 0 2995 + ], 2996 + "source": [ 2997 + "obj-sel-target-zero", 2998 + 1 2999 + ] 3000 + } 3001 + }, 3002 + { 3003 + "patchline": { 3004 + "destination": [ 3005 + "obj-print-target-id", 3006 + 0 3007 + ], 3008 + "source": [ 3009 + "obj-target-trigger", 3010 + 3 3011 + ] 3012 + } 3013 + }, 3014 + { 3015 + "patchline": { 3016 + "destination": [ 3017 + "obj-prepend-target-id", 3018 + 0 3019 + ], 3020 + "source": [ 3021 + "obj-target-trigger", 3022 + 3 3023 + ] 3024 + } 3025 + }, 3026 + { 3027 + "patchline": { 3028 + "destination": [ 3029 + "obj-msg-getname", 3030 + 0 3031 + ], 3032 + "source": [ 3033 + "obj-target-trigger", 3034 + 2 3035 + ] 3036 + } 3037 + }, 3038 + { 3039 + "patchline": { 3040 + "destination": [ 3041 + "obj-msg-getmax", 3042 + 0 3043 + ], 3044 + "source": [ 3045 + "obj-target-trigger", 3046 + 1 3047 + ] 3048 + } 3049 + }, 3050 + { 3051 + "patchline": { 3052 + "destination": [ 3053 + "obj-msg-getmin", 3054 + 0 3055 + ], 3056 + "source": [ 3057 + "obj-target-trigger", 3058 + 0 3059 + ] 3060 + } 3061 + }, 3062 + { 3063 + "patchline": { 3064 + "destination": [ 3065 + "obj-reader-min", 3066 + 1 3067 + ], 3068 + "source": [ 3069 + "obj-prepend-target-id", 3070 + 0 3071 + ] 3072 + } 3073 + }, 3074 + { 3075 + "patchline": { 3076 + "destination": [ 3077 + "obj-reader-max", 3078 + 1 3079 + ], 3080 + "source": [ 3081 + "obj-prepend-target-id", 3082 + 0 3083 + ] 3084 + } 3085 + }, 3086 + { 3087 + "patchline": { 3088 + "destination": [ 3089 + "obj-reader-name", 3090 + 1 3091 + ], 3092 + "source": [ 3093 + "obj-prepend-target-id", 3094 + 0 3095 + ] 3096 + } 3097 + }, 3098 + { 3099 + "patchline": { 3100 + "destination": [ 3101 + "obj-writer", 3102 + 1 3103 + ], 3104 + "source": [ 3105 + "obj-prepend-target-id", 3106 + 0 3107 + ] 3108 + } 3109 + }, 3110 + { 3111 + "patchline": { 3112 + "destination": [ 3113 + "obj-route-target-id", 3114 + 0 3115 + ], 3116 + "source": [ 3117 + "obj-songpath", 3118 + 0 3119 + ] 3120 + } 3121 + }, 3122 + { 3123 + "patchline": { 3124 + "destination": [ 3125 + "obj-route-target-id", 3126 + 0 3127 + ], 3128 + "source": [ 3129 + "obj-songpath", 3130 + 1 3131 + ] 3132 + } 3133 + }, 3134 + { 3135 + "patchline": { 3136 + "destination": [ 3137 + "obj-print-path-msg", 3138 + 0 3139 + ], 3140 + "source": [ 3141 + "obj-songpath", 3142 + 0 3143 + ] 3144 + } 3145 + }, 3146 + { 3147 + "patchline": { 3148 + "destination": [ 3149 + "obj-print-path-id", 3150 + 0 3151 + ], 3152 + "source": [ 3153 + "obj-songpath", 3154 + 1 3155 + ] 3156 + } 3157 + }, 3158 + { 3159 + "patchline": { 3160 + "destination": [ 3161 + "obj-reader-min", 3162 + 0 3163 + ], 3164 + "source": [ 3165 + "obj-msg-getmin", 3166 + 0 3167 + ] 3168 + } 3169 + }, 3170 + { 3171 + "patchline": { 3172 + "destination": [ 3173 + "obj-reader-max", 3174 + 0 3175 + ], 3176 + "source": [ 3177 + "obj-msg-getmax", 3178 + 0 3179 + ] 3180 + } 3181 + }, 3182 + { 3183 + "patchline": { 3184 + "destination": [ 3185 + "obj-reader-name", 3186 + 0 3187 + ], 3188 + "source": [ 3189 + "obj-msg-getname", 3190 + 0 3191 + ] 3192 + } 3193 + }, 3194 + { 3195 + "patchline": { 3196 + "destination": [ 3197 + "obj-route-min", 3198 + 0 3199 + ], 3200 + "source": [ 3201 + "obj-reader-min", 3202 + 0 3203 + ] 3204 + } 3205 + }, 3206 + { 3207 + "patchline": { 3208 + "destination": [ 3209 + "obj-route-max", 3210 + 0 3211 + ], 3212 + "source": [ 3213 + "obj-reader-max", 3214 + 0 3215 + ] 3216 + } 3217 + }, 3218 + { 3219 + "patchline": { 3220 + "destination": [ 3221 + "obj-store-min", 3222 + 0 3223 + ], 3224 + "source": [ 3225 + "obj-route-min", 3226 + 0 3227 + ] 3228 + } 3229 + }, 3230 + { 3231 + "patchline": { 3232 + "destination": [ 3233 + "obj-store-max", 3234 + 0 3235 + ], 3236 + "source": [ 3237 + "obj-route-max", 3238 + 0 3239 + ] 3240 + } 3241 + }, 3242 + { 3243 + "patchline": { 3244 + "destination": [ 3245 + "obj-print-min", 3246 + 0 3247 + ], 3248 + "source": [ 3249 + "obj-reader-min", 3250 + 0 3251 + ] 3252 + } 3253 + }, 3254 + { 3255 + "patchline": { 3256 + "destination": [ 3257 + "obj-print-max", 3258 + 0 3259 + ], 3260 + "source": [ 3261 + "obj-reader-max", 3262 + 0 3263 + ] 3264 + } 3265 + }, 3266 + { 3267 + "patchline": { 3268 + "destination": [ 3269 + "obj-expr", 3270 + 2 3271 + ], 3272 + "source": [ 3273 + "obj-store-min", 3274 + 0 3275 + ] 3276 + } 3277 + }, 3278 + { 3279 + "patchline": { 3280 + "destination": [ 3281 + "obj-expr", 3282 + 1 3283 + ], 3284 + "source": [ 3285 + "obj-store-max", 3286 + 0 3287 + ] 3288 + } 3289 + }, 3290 + { 3291 + "patchline": { 3292 + "destination": [ 3293 + "obj-clip", 3294 + 1 3295 + ], 3296 + "source": [ 3297 + "obj-store-min", 3298 + 0 3299 + ] 3300 + } 3301 + }, 3302 + { 3303 + "patchline": { 3304 + "destination": [ 3305 + "obj-clip", 3306 + 2 3307 + ], 3308 + "source": [ 3309 + "obj-store-max", 3310 + 0 3311 + ] 3312 + } 3313 + }, 3314 + { 3315 + "patchline": { 3316 + "destination": [ 3317 + "obj-sprintf-range", 3318 + 0 3319 + ], 3320 + "source": [ 3321 + "obj-store-min", 3322 + 0 3323 + ] 3324 + } 3325 + }, 3326 + { 3327 + "patchline": { 3328 + "destination": [ 3329 + "obj-sprintf-range", 3330 + 1 3331 + ], 3332 + "source": [ 3333 + "obj-store-max", 3334 + 0 3335 + ] 3336 + } 3337 + }, 3338 + { 3339 + "patchline": { 3340 + "destination": [ 3341 + "obj-prepend-set-range", 3342 + 0 3343 + ], 3344 + "source": [ 3345 + "obj-sprintf-range", 3346 + 0 3347 + ] 3348 + } 3349 + }, 3350 + { 3351 + "patchline": { 3352 + "destination": [ 3353 + "obj-range-display", 3354 + 0 3355 + ], 3356 + "source": [ 3357 + "obj-prepend-set-range", 3358 + 0 3359 + ] 3360 + } 3361 + }, 3362 + { 3363 + "patchline": { 3364 + "destination": [ 3365 + "obj-route-name", 3366 + 0 3367 + ], 3368 + "source": [ 3369 + "obj-reader-name", 3370 + 0 3371 + ] 3372 + } 3373 + }, 3374 + { 3375 + "patchline": { 3376 + "destination": [ 3377 + "obj-target-name-symbol", 3378 + 0 3379 + ], 3380 + "source": [ 3381 + "obj-route-name", 3382 + 0 3383 + ] 3384 + } 3385 + }, 3386 + { 3387 + "patchline": { 3388 + "destination": [ 3389 + "obj-ui-target-msg", 3390 + 0 3391 + ], 3392 + "source": [ 3393 + "obj-target-name-symbol", 3394 + 0 3395 + ] 3396 + } 3397 + }, 3398 + { 3399 + "patchline": { 3400 + "destination": [ 3401 + "obj-jweb", 3402 + 0 3403 + ], 3404 + "source": [ 3405 + "obj-ui-target-msg", 3406 + 0 3407 + ] 3408 + } 3409 + }, 3410 + { 3411 + "patchline": { 3412 + "destination": [ 3413 + "obj-prepend-set-status", 3414 + 0 3415 + ], 3416 + "source": [ 3417 + "obj-route-name", 3418 + 0 3419 + ] 3420 + } 3421 + }, 3422 + { 3423 + "patchline": { 3424 + "destination": [ 3425 + "obj-status", 3426 + 0 3427 + ], 3428 + "source": [ 3429 + "obj-prepend-set-status", 3430 + 0 3431 + ] 3432 + } 3433 + }, 3434 + { 3435 + "patchline": { 3436 + "destination": [ 3437 + "obj-print", 3438 + 0 3439 + ], 3440 + "source": [ 3441 + "obj-reader-name", 3442 + 0 3443 + ] 3444 + } 3445 + }, 3446 + { 3447 + "patchline": { 3448 + "destination": [ 3449 + "obj-print-name", 3450 + 0 3451 + ], 3452 + "source": [ 3453 + "obj-reader-name", 3454 + 0 3455 + ] 3456 + } 3457 + }, 3458 + { 3459 + "patchline": { 3460 + "destination": [ 3461 + "obj-stripnote", 3462 + 0 3463 + ], 3464 + "source": [ 3465 + "obj-unpack-note", 3466 + 0 3467 + ] 3468 + } 3469 + }, 3470 + { 3471 + "patchline": { 3472 + "destination": [ 3473 + "obj-stripnote", 3474 + 1 3475 + ], 3476 + "source": [ 3477 + "obj-unpack-note", 3478 + 1 3479 + ] 3480 + } 3481 + }, 3482 + { 3483 + "patchline": { 3484 + "destination": [ 3485 + "obj-print-note", 3486 + 0 3487 + ], 3488 + "source": [ 3489 + "obj-unpack-note", 3490 + 0 3491 + ] 3492 + } 3493 + }, 3494 + { 3495 + "patchline": { 3496 + "destination": [ 3497 + "obj-print-velocity", 3498 + 0 3499 + ], 3500 + "source": [ 3501 + "obj-unpack-note", 3502 + 1 3503 + ] 3504 + } 3505 + }, 3506 + { 3507 + "patchline": { 3508 + "destination": [ 3509 + "obj-print-channel", 3510 + 0 3511 + ], 3512 + "source": [ 3513 + "obj-midiparse", 3514 + 6 3515 + ] 3516 + } 3517 + }, 3518 + { 3519 + "patchline": { 3520 + "destination": [ 3521 + "obj-note-range-clip", 3522 + 0 3523 + ], 3524 + "source": [ 3525 + "obj-stripnote", 3526 + 0 3527 + ] 3528 + } 3529 + }, 3530 + { 3531 + "patchline": { 3532 + "destination": [ 3533 + "obj-expr", 3534 + 0 3535 + ], 3536 + "source": [ 3537 + "obj-note-range-clip", 3538 + 0 3539 + ] 3540 + } 3541 + }, 3542 + { 3543 + "patchline": { 3544 + "destination": [ 3545 + "obj-clip", 3546 + 0 3547 + ], 3548 + "source": [ 3549 + "obj-stripnote", 3550 + 0 3551 + ] 3552 + } 3553 + }, 3554 + { 3555 + "patchline": { 3556 + "destination": [ 3557 + "obj-ui-note-msg", 3558 + 0 3559 + ], 3560 + "source": [ 3561 + "obj-stripnote", 3562 + 0 3563 + ] 3564 + } 3565 + }, 3566 + { 3567 + "patchline": { 3568 + "destination": [ 3569 + "obj-jweb", 3570 + 0 3571 + ], 3572 + "source": [ 3573 + "obj-ui-note-msg", 3574 + 0 3575 + ] 3576 + } 3577 + }, 3578 + { 3579 + "patchline": { 3580 + "destination": [ 3581 + "obj-print-stripnote-note", 3582 + 0 3583 + ], 3584 + "source": [ 3585 + "obj-stripnote", 3586 + 0 3587 + ] 3588 + } 3589 + }, 3590 + { 3591 + "patchline": { 3592 + "destination": [ 3593 + "obj-print-stripnote-velocity", 3594 + 0 3595 + ], 3596 + "source": [ 3597 + "obj-stripnote", 3598 + 1 3599 + ] 3600 + } 3601 + }, 3602 + { 3603 + "patchline": { 3604 + "destination": [ 3605 + "obj-prepend-set-note", 3606 + 0 3607 + ], 3608 + "source": [ 3609 + "obj-stripnote", 3610 + 0 3611 + ] 3612 + } 3613 + }, 3614 + { 3615 + "patchline": { 3616 + "destination": [ 3617 + "obj-note-display", 3618 + 0 3619 + ], 3620 + "source": [ 3621 + "obj-prepend-set-note", 3622 + 0 3623 + ] 3624 + } 3625 + }, 3626 + { 3627 + "patchline": { 3628 + "destination": [ 3629 + "obj-sprintf-midi-note", 3630 + 0 3631 + ], 3632 + "source": [ 3633 + "obj-unpack-note", 3634 + 0 3635 + ] 3636 + } 3637 + }, 3638 + { 3639 + "patchline": { 3640 + "destination": [ 3641 + "obj-sprintf-midi-note", 3642 + 1 3643 + ], 3644 + "source": [ 3645 + "obj-unpack-note", 3646 + 1 3647 + ] 3648 + } 3649 + }, 3650 + { 3651 + "patchline": { 3652 + "destination": [ 3653 + "obj-prepend-set-midi-display", 3654 + 0 3655 + ], 3656 + "source": [ 3657 + "obj-sprintf-midi-note", 3658 + 0 3659 + ] 3660 + } 3661 + }, 3662 + { 3663 + "patchline": { 3664 + "destination": [ 3665 + "obj-midi-display", 3666 + 0 3667 + ], 3668 + "source": [ 3669 + "obj-prepend-set-midi-display", 3670 + 0 3671 + ] 3672 + } 3673 + }, 3674 + { 3675 + "patchline": { 3676 + "destination": [ 3677 + "obj-gate-scaled", 3678 + 1 3679 + ], 3680 + "source": [ 3681 + "obj-expr", 3682 + 0 3683 + ] 3684 + } 3685 + }, 3686 + { 3687 + "patchline": { 3688 + "destination": [ 3689 + "obj-gate-direct", 3690 + 1 3691 + ], 3692 + "source": [ 3693 + "obj-clip", 3694 + 0 3695 + ] 3696 + } 3697 + }, 3698 + { 3699 + "patchline": { 3700 + "destination": [ 3701 + "obj-print-scaled", 3702 + 0 3703 + ], 3704 + "source": [ 3705 + "obj-expr", 3706 + 0 3707 + ] 3708 + } 3709 + }, 3710 + { 3711 + "patchline": { 3712 + "destination": [ 3713 + "obj-print-direct", 3714 + 0 3715 + ], 3716 + "source": [ 3717 + "obj-clip", 3718 + 0 3719 + ] 3720 + } 3721 + }, 3722 + { 3723 + "patchline": { 3724 + "destination": [ 3725 + "obj-tofloat", 3726 + 0 3727 + ], 3728 + "source": [ 3729 + "obj-gate-scaled", 3730 + 0 3731 + ] 3732 + } 3733 + }, 3734 + { 3735 + "patchline": { 3736 + "destination": [ 3737 + "obj-tofloat", 3738 + 0 3739 + ], 3740 + "source": [ 3741 + "obj-gate-direct", 3742 + 0 3743 + ] 3744 + } 3745 + }, 3746 + { 3747 + "patchline": { 3748 + "destination": [ 3749 + "obj-master-gate", 3750 + 1 3751 + ], 3752 + "source": [ 3753 + "obj-tofloat", 3754 + 0 3755 + ] 3756 + } 3757 + }, 3758 + { 3759 + "patchline": { 3760 + "destination": [ 3761 + "obj-msg-setval", 3762 + 0 3763 + ], 3764 + "source": [ 3765 + "obj-master-gate", 3766 + 0 3767 + ] 3768 + } 3769 + }, 3770 + { 3771 + "patchline": { 3772 + "destination": [ 3773 + "obj-writer", 3774 + 0 3775 + ], 3776 + "source": [ 3777 + "obj-msg-setval", 3778 + 0 3779 + ] 3780 + } 3781 + }, 3782 + { 3783 + "patchline": { 3784 + "destination": [ 3785 + "obj-print-write", 3786 + 0 3787 + ], 3788 + "source": [ 3789 + "obj-master-gate", 3790 + 0 3791 + ] 3792 + } 3793 + }, 3794 + { 3795 + "patchline": { 3796 + "destination": [ 3797 + "obj-ui-value-msg", 3798 + 0 3799 + ], 3800 + "source": [ 3801 + "obj-master-gate", 3802 + 0 3803 + ] 3804 + } 3805 + }, 3806 + { 3807 + "patchline": { 3808 + "destination": [ 3809 + "obj-jweb", 3810 + 0 3811 + ], 3812 + "source": [ 3813 + "obj-ui-value-msg", 3814 + 0 3815 + ] 3816 + } 3817 + }, 3818 + { 3819 + "patchline": { 3820 + "destination": [ 3821 + "obj-print-setval", 3822 + 0 3823 + ], 3824 + "source": [ 3825 + "obj-msg-setval", 3826 + 0 3827 + ] 3828 + } 3829 + }, 3830 + { 3831 + "patchline": { 3832 + "destination": [ 3833 + "obj-print-writer", 3834 + 0 3835 + ], 3836 + "source": [ 3837 + "obj-writer", 3838 + 0 3839 + ] 3840 + } 3841 + }, 3842 + { 3843 + "patchline": { 3844 + "destination": [ 3845 + "obj-prepend-set-val", 3846 + 0 3847 + ], 3848 + "source": [ 3849 + "obj-master-gate", 3850 + 0 3851 + ] 3852 + } 3853 + }, 3854 + { 3855 + "patchline": { 3856 + "destination": [ 3857 + "obj-val-display", 3858 + 0 3859 + ], 3860 + "source": [ 3861 + "obj-prepend-set-val", 3862 + 0 3863 + ] 3864 + } 3865 + }, 3866 + { 3867 + "patchline": { 3868 + "destination": [ 3869 + "obj-open-gate", 3870 + 0 3871 + ], 3872 + "source": [ 3873 + "obj-route-name", 3874 + 0 3875 + ] 3876 + } 3877 + }, 3878 + { 3879 + "patchline": { 3880 + "destination": [ 3881 + "obj-master-gate", 3882 + 0 3883 + ], 3884 + "source": [ 3885 + "obj-open-gate", 3886 + 0 3887 + ] 3888 + } 3889 + }, 3890 + { 3891 + "patchline": { 3892 + "destination": [ 3893 + "obj-gate-scaled", 3894 + 0 3895 + ], 3896 + "source": [ 3897 + "obj-mode-sel", 3898 + 0 3899 + ] 3900 + } 3901 + }, 3902 + { 3903 + "patchline": { 3904 + "destination": [ 3905 + "obj-gate-direct", 3906 + 0 3907 + ], 3908 + "source": [ 3909 + "obj-mode-sel", 3910 + 1 3911 + ] 3912 + } 3913 + }, 3914 + { 3915 + "patchline": { 3916 + "destination": [ 3917 + "obj-mode-sel", 3918 + 0 3919 + ], 3920 + "source": [ 3921 + "obj-mode", 3922 + 0 3923 + ] 3924 + } 3925 + }, 3926 + { 3927 + "patchline": { 3928 + "destination": [ 3929 + "obj-print-mode", 3930 + 0 3931 + ], 3932 + "source": [ 3933 + "obj-mode", 3934 + 0 3935 + ] 3936 + } 3937 + }, 3938 + { 3939 + "patchline": { 3940 + "destination": [ 3941 + "obj-midiout", 3942 + 0 3943 + ], 3944 + "source": [ 3945 + "obj-midiin", 3946 + 0 3947 + ] 3948 + } 3949 + }, 3950 + { 3951 + "patchline": { 3952 + "destination": [ 3953 + "obj-print-midi", 3954 + 0 3955 + ], 3956 + "source": [ 3957 + "obj-midiin", 3958 + 0 3959 + ] 3960 + } 3961 + }, 3962 + { 3963 + "patchline": { 3964 + "destination": [ 3965 + "obj-sprintf-midi-raw", 3966 + 0 3967 + ], 3968 + "source": [ 3969 + "obj-midiin", 3970 + 0 3971 + ] 3972 + } 3973 + }, 3974 + { 3975 + "patchline": { 3976 + "destination": [ 3977 + "obj-prepend-set-midi-display", 3978 + 0 3979 + ], 3980 + "source": [ 3981 + "obj-sprintf-midi-raw", 3982 + 0 3983 + ] 3984 + } 3985 + }, 3986 + { 3987 + "patchline": { 3988 + "destination": [ 3989 + "obj-midiparse", 3990 + 0 3991 + ], 3992 + "source": [ 3993 + "obj-midiin", 3994 + 0 3995 + ] 3996 + } 3997 + }, 3998 + { 3999 + "patchline": { 4000 + "destination": [ 4001 + "obj-unpack-note", 4002 + 0 4003 + ], 4004 + "source": [ 4005 + "obj-midiparse", 4006 + 0 4007 + ] 4008 + } 4009 + }, 4010 + { 4011 + "patchline": { 4012 + "source": [ 4013 + "obj-device-path", 4014 + 0 4015 + ], 4016 + "destination": [ 4017 + "obj-device-observer", 4018 + 1 4019 + ] 4020 + } 4021 + }, 4022 + { 4023 + "patchline": { 4024 + "source": [ 4025 + "obj-jweb-route", 4026 + 0 4027 + ], 4028 + "destination": [ 4029 + "obj-device-path", 4030 + 0 4031 + ] 4032 + } 4033 + } 671 4034 ], 672 - 673 4035 "dependency_cache": [], 674 4036 "latency": 0, 675 4037 "is_mpe": 0,
+3 -2
ac-m4l/build.py
··· 752 752 return 753 753 754 754 # Build output directory 755 - output_dir = Path(__file__).parent 755 + output_dir = Path(__file__).parent / "outputs" 756 + output_dir.mkdir(parents=True, exist_ok=True) 756 757 757 758 # Print build mode 758 759 mode = "PROD → https://aesthetic.computer" if production else f"DEV → {defaults.get('baseUrl', 'https://localhost:8888')}" ··· 789 790 f.write(data) 790 791 791 792 print(f"\n🔧 {filename} [{type_label}]") 792 - print(f" ✅ Built: {filename} ({len(data)} bytes)") 793 + print(f" ✅ Built: {output_path} ({len(data)} bytes)") 793 794 built.append((filename, output_path)) 794 795 795 796 # Install if requested
+439 -137
system/public/aesthetic.computer/disks/ableton.mjs
··· 1 - // Ableton - AC Max for Live Plugin Manager, 2026.02.12 2 - // Browse and download Max for Live devices 1 + // Ableton - AC Max for Live device browser + builder, 2026.03.31 3 2 4 3 let plugins = []; 5 4 let loading = true; 6 5 let error = null; 7 - let downloadButtons = []; 8 - let hoveredButton = null; 9 6 let frame = 0; 10 - let downloading = null; // plugin code currently downloading 7 + let downloading = null; 8 + let hoverId = null; 9 + let regions = []; 10 + let needsPaintRef = null; 11 11 12 - const { sin, cos, PI, floor, abs } = Math; 12 + let customPiece = "notepat"; 13 + let customPieceOptions = ["notepat"]; 14 + let customPieceIndex = 0; 15 + let customBusy = null; 16 + let customMessage = ""; 13 17 14 - export const boot = () => { 15 - fetch("/m4l-plugins") 16 - .then((res) => { 17 - if (!res.ok) throw new Error(`HTTP ${res.status}`); 18 - return res.json(); 19 - }) 20 - .then((data) => { 21 - plugins = Array.isArray(data) ? data : []; 22 - loading = false; 23 - }) 24 - .catch((err) => { 25 - console.error("Failed to load plugins:", err); 26 - error = "Failed to load plugins."; 27 - loading = false; 28 - }); 29 - }; 18 + const { sin, cos, floor, abs, max } = Math; 30 19 31 - function sim() { 32 - frame += 1; 20 + function stripEmoji(str = "") { 21 + return str.replace(/[\u{1F000}-\u{1FFFF}]/gu, "").trim(); 33 22 } 34 23 35 - // Strip emoji (AC bitmap font can't render them) 36 - function stripEmoji(str) { 37 - return str.replace(/[\u{1F000}-\u{1FFFF}]/gu, "").trim(); 24 + function playClick(sound) { 25 + sound?.synth?.({ type: "sine", tone: 660, beats: 0.05, volume: 0.2 }); 26 + sound?.synth?.({ type: "sine", tone: 880, beats: 0.05, volume: 0.15, delay: 0.05 }); 38 27 } 39 28 40 - // Draw animated waveform header 29 + function playError(sound) { 30 + sound?.synth?.({ type: "square", tone: 180, beats: 0.08, volume: 0.16 }); 31 + } 32 + 33 + function bytesToBinaryString(bytes) { 34 + let binary = ""; 35 + const chunk = 8192; 36 + for (let i = 0; i < bytes.length; i += chunk) { 37 + binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk)); 38 + } 39 + return binary; 40 + } 41 + 42 + function syncCustomPieceOptions() { 43 + const pieces = []; 44 + for (const plugin of plugins) { 45 + const piece = plugin?.metadata?.piece; 46 + if (piece && !pieces.includes(piece)) pieces.push(piece); 47 + } 48 + 49 + if (customPiece && !pieces.includes(customPiece)) { 50 + pieces.unshift(customPiece); 51 + } 52 + 53 + if (pieces.length === 0) pieces.push("notepat"); 54 + 55 + customPieceOptions = pieces; 56 + customPieceIndex = max(0, customPieceOptions.indexOf(customPiece)); 57 + customPiece = customPieceOptions[customPieceIndex] || "notepat"; 58 + } 59 + 60 + function setCustomPiece(piece) { 61 + if (!piece) return; 62 + customPiece = piece; 63 + syncCustomPieceOptions(); 64 + } 65 + 66 + function cycleCustomPiece(step = 1) { 67 + if (!customPieceOptions.length) return; 68 + customPieceIndex = (customPieceIndex + step + customPieceOptions.length) % customPieceOptions.length; 69 + customPiece = customPieceOptions[customPieceIndex]; 70 + } 71 + 41 72 function drawWave(ink, line, width, y, dark, t) { 42 73 const amp = 3; 43 74 const wavColor = dark ? [40, 60, 100] : [180, 200, 230]; 44 75 for (let x = 0; x < width; x += 2) { 45 76 const v = sin((x * 0.04) + t * 0.03) * amp + 46 - sin((x * 0.07) - t * 0.02) * amp * 0.5; 77 + sin((x * 0.07) - t * 0.02) * amp * 0.5; 47 78 ink(...wavColor, 80 + abs(v) * 15); 48 79 line(x, y + v, x + 1, y + v); 49 80 } 50 81 } 51 82 52 - // Pixel icon: mini keyboard (midi) 53 - function iconMidi(ink, box, x, y, t) { 83 + function iconMidi(ink, x, y, t) { 54 84 const pulse = sin(t * 0.08) * 0.3 + 0.7; 55 85 const b = floor(180 * pulse); 56 86 ink(b, b, 255).box(x, y, 9, 7); ··· 58 88 ink(0).box(x + 5, y, 1, 4); 59 89 } 60 90 61 - // Pixel icon: waveform (instrument) 62 91 function iconInstrument(ink, plot, x, y, t) { 63 92 for (let i = 0; i < 9; i++) { 64 93 const v = sin((i * 0.8) + t * 0.06) * 3; ··· 66 95 } 67 96 } 68 97 69 - // Pixel icon: knob (effect) 70 98 function iconEffect(ink, circle, line, x, y, t) { 71 99 const angle = t * 0.04; 72 100 ink(255, 150, 80).circle(x + 4, y + 3, 3, false); ··· 75 103 ink(255, 200, 100).line(x + 4, y + 3, x + 4 + dx, y + 3 + dy); 76 104 } 77 105 78 - function paint({ wipe, ink, screen, box, line, circle, plot, pen, dark }) { 106 + function buildOnlinePatcher(pieceRef) { 107 + const displayName = pieceRef; 108 + const liveUrl = `https://aesthetic.computer/${pieceRef}?nogap=true&daw=true&nolabel=true`; 109 + const width = 400; 110 + const height = 200; 111 + const patcher = { 112 + patcher: { 113 + fileversion: 1, 114 + appversion: { major: 9, minor: 0, revision: 7, architecture: "x64", modernui: 1 }, 115 + classnamespace: "box", 116 + rect: [134, 174, 800, 600], 117 + openrect: [0, 0, width, height], 118 + openinpresentation: 1, 119 + gridsize: [15, 15], 120 + enablehscroll: 0, 121 + enablevscroll: 0, 122 + devicewidth: width, 123 + description: `Aesthetic Computer ${displayName} (online)`, 124 + boxes: [ 125 + { 126 + box: { 127 + disablefind: 0, 128 + id: "obj-jweb", 129 + latency: 0, 130 + maxclass: "jweb~", 131 + numinlets: 1, 132 + numoutlets: 3, 133 + outlettype: ["signal", "signal", ""], 134 + patching_rect: [10, 50, width, height], 135 + presentation: 1, 136 + presentation_rect: [0, 0, width + 1, height + 1], 137 + rendermode: 1, 138 + url: liveUrl, 139 + }, 140 + }, 141 + { 142 + box: { 143 + id: "obj-plugout", 144 + maxclass: "newobj", 145 + numinlets: 2, 146 + numoutlets: 0, 147 + patching_rect: [10, 280, 75, 22], 148 + text: "plugout~ 1 2", 149 + }, 150 + }, 151 + { 152 + box: { 153 + id: "obj-thisdevice", 154 + maxclass: "newobj", 155 + numinlets: 1, 156 + numoutlets: 3, 157 + outlettype: ["bang", "int", "int"], 158 + patching_rect: [350, 50, 85, 22], 159 + text: "live.thisdevice", 160 + }, 161 + }, 162 + { 163 + box: { 164 + id: "obj-print", 165 + maxclass: "newobj", 166 + numinlets: 1, 167 + numoutlets: 0, 168 + patching_rect: [350, 80, 170, 22], 169 + text: `print [AC-${displayName.toUpperCase()}]`, 170 + }, 171 + }, 172 + { 173 + box: { 174 + id: "obj-route", 175 + maxclass: "newobj", 176 + numinlets: 1, 177 + numoutlets: 2, 178 + outlettype: ["", ""], 179 + patching_rect: [350, 140, 60, 22], 180 + text: "route ready", 181 + }, 182 + }, 183 + { 184 + box: { 185 + id: "obj-activate", 186 + maxclass: "message", 187 + numinlets: 2, 188 + numoutlets: 1, 189 + outlettype: [""], 190 + patching_rect: [350, 170, 60, 22], 191 + text: "activate 1", 192 + }, 193 + }, 194 + ], 195 + lines: [ 196 + { patchline: { destination: ["obj-plugout", 0], source: ["obj-jweb", 0] } }, 197 + { patchline: { destination: ["obj-plugout", 1], source: ["obj-jweb", 1] } }, 198 + { patchline: { destination: ["obj-print", 0], source: ["obj-thisdevice", 0] } }, 199 + { patchline: { destination: ["obj-route", 0], source: ["obj-jweb", 2] } }, 200 + { patchline: { destination: ["obj-activate", 0], source: ["obj-route", 0] } }, 201 + { patchline: { destination: ["obj-jweb", 0], source: ["obj-activate", 0] } }, 202 + ], 203 + dependency_cache: [], 204 + latency: 0, 205 + is_mpe: 0, 206 + external_mpe_tuning_enabled: 0, 207 + minimum_live_version: "", 208 + minimum_max_version: "", 209 + platform_compatibility: 0, 210 + autosave: 0, 211 + }, 212 + }; 213 + 214 + const header = "ampf\x04\x00\x00\x00iiiimeta\x04\x00\x00\x00\x00\x00\x00\x00ptch"; 215 + const patcherJson = JSON.stringify(patcher); 216 + const jsonBytes = new TextEncoder().encode(patcherJson); 217 + const headerBytes = new Uint8Array([...header].map((char) => char.charCodeAt(0))); 218 + const lenBytes = new Uint8Array(4); 219 + new DataView(lenBytes.buffer).setUint32(0, jsonBytes.length, true); 220 + const binary = new Uint8Array(headerBytes.length + lenBytes.length + jsonBytes.length); 221 + binary.set(headerBytes, 0); 222 + binary.set(lenBytes, headerBytes.length); 223 + binary.set(jsonBytes, headerBytes.length + lenBytes.length); 224 + return binary; 225 + } 226 + 227 + async function buildOfflineDevice(pieceRef, download) { 228 + const isKidlisp = pieceRef.startsWith("$"); 229 + const query = isKidlisp 230 + ? `code=${encodeURIComponent(pieceRef)}` 231 + : `piece=${encodeURIComponent(pieceRef)}`; 232 + const response = await fetch(`/api/pack-html?${query}&format=m4d`); 233 + if (!response.ok) { 234 + const err = await response.json().catch(() => ({ error: `HTTP ${response.status}` })); 235 + throw new Error(err.error || `M4D build failed (${response.status})`); 236 + } 237 + const bytes = new Uint8Array(await response.arrayBuffer()); 238 + download(`AC ${pieceRef} (offline).amxd`, bytesToBinaryString(bytes), { encoding: "binary" }); 239 + } 240 + 241 + async function buildOnlineDevice(pieceRef, download) { 242 + const bytes = buildOnlinePatcher(pieceRef); 243 + download(`AC ${pieceRef} (online).amxd`, bytesToBinaryString(bytes), { encoding: "binary" }); 244 + } 245 + 246 + function registerRegion(id, x, y, w, h, action) { 247 + regions.push({ id, x, y, w, h, action }); 248 + } 249 + 250 + function hitRegion(pen) { 251 + if (!pen) return null; 252 + for (const region of regions) { 253 + if ( 254 + pen.x >= region.x && 255 + pen.x < region.x + region.w && 256 + pen.y >= region.y && 257 + pen.y < region.y + region.h 258 + ) { 259 + return region; 260 + } 261 + } 262 + return null; 263 + } 264 + 265 + function drawButton({ ink, box }, region, label, colors, hovered) { 266 + const { x, y, w, h } = region; 267 + const [bg, border, text] = colors; 268 + ink(...bg).box(x, y, w, h); 269 + ink(...border).box(x, y, w, h, "outline"); 270 + if (hovered) { 271 + ink(255, 255, 255, 22).box(x, y, w, h); 272 + } 273 + ink(...text).write(label, { x: x + 4, y: y + 3 }); 274 + } 275 + 276 + export function boot({ params, needsPaint }) { 277 + needsPaintRef = needsPaint; 278 + if (params?.[0]) setCustomPiece(params[0]); 279 + 280 + fetch("/m4l-plugins") 281 + .then((res) => { 282 + if (!res.ok) throw new Error(`HTTP ${res.status}`); 283 + return res.json(); 284 + }) 285 + .then((data) => { 286 + plugins = Array.isArray(data) ? data : []; 287 + syncCustomPieceOptions(); 288 + loading = false; 289 + needsPaintRef?.(); 290 + }) 291 + .catch((err) => { 292 + console.error("Failed to load plugins:", err); 293 + error = "Failed to load plugins."; 294 + loading = false; 295 + needsPaintRef?.(); 296 + }); 297 + } 298 + 299 + function sim() { 300 + frame += 1; 301 + } 302 + 303 + function paint({ wipe, ink, screen, box, line, circle, plot, dark }) { 79 304 const bg = dark ? [15, 15, 20] : [242, 242, 238]; 80 305 const fg = dark ? 240 : 10; 81 - const dim = dark ? 80 : 140; 306 + const dim = dark ? 95 : 135; 82 307 const accent = dark ? [100, 170, 255] : [20, 80, 200]; 83 - const btnBg = dark ? [40, 80, 160] : [30, 90, 200]; 84 - const btnHover = dark ? [70, 140, 255] : [50, 120, 255]; 85 308 const sep = dark ? 30 : 215; 86 - const cardDim = dark ? 120 : 110; 309 + const btn = { 310 + primary: [dark ? [36, 86, 165] : [30, 90, 200], dark ? [90, 160, 255] : [50, 120, 255], [255, 255, 255]], 311 + alt: [dark ? [35, 58, 40] : [215, 236, 220], dark ? [95, 170, 115] : [70, 150, 90], dark ? [180, 255, 195] : [20, 95, 35]], 312 + warm: [dark ? [52, 42, 24] : [242, 228, 208], dark ? [220, 165, 80] : [185, 125, 35], dark ? [255, 225, 170] : [105, 68, 12]], 313 + danger: [dark ? [70, 30, 35] : [245, 220, 225], dark ? [220, 110, 130] : [190, 85, 105], dark ? [255, 190, 205] : [115, 38, 52]], 314 + }; 87 315 88 316 wipe(...bg); 317 + regions = []; 89 318 90 319 const { width } = screen; 91 320 const margin = 12; 321 + const contentWidth = width - margin * 2; 322 + const cardWidth = max(120, contentWidth); 92 323 let y = 20; 93 324 94 - // Animated wave behind header 95 325 drawWave(ink, line, width, y + 4, dark, frame); 96 326 97 - // Header 98 327 ink(fg).write("AC Max for Live", { x: margin, y }); 99 328 y += 12; 100 329 101 - // Subtle pulsing subtitle 102 - const subAlpha = floor(dim + sin(frame * 0.03) * 20); 103 - ink(subAlpha).write("Download devices for Ableton Live", { x: margin, y }); 330 + ink(dim).write("Downloads + instrument builder for Ableton Live", { x: margin, y }); 104 331 y += 14; 105 332 106 - // Animated separator - a dot travels along the line 107 333 ink(sep).line(margin, y, width - margin, y); 108 334 const dotX = margin + ((frame * 0.5) % (width - margin * 2)); 109 335 ink(...accent).plot(floor(dotX), y); 110 336 ink(...accent).plot(floor(dotX) + 1, y); 111 337 y += 12; 112 338 339 + ink(...(dark ? [22, 24, 34] : [228, 232, 242])).box(0, y - 4, width, 74); 340 + ink(...accent).write("Custom Instrument Builder", { x: margin, y }); 341 + y += 12; 342 + 343 + const currentPieceLabel = `piece ${customPiece}`; 344 + ink(dim).write("Cycle pieces or open ableton <piece> for any target.", { x: margin, y }); 345 + y += 13; 346 + 347 + const pieceBtn = { id: "builder-piece", x: margin, y, w: max(92, currentPieceLabel.length * 6 + 10), h: 14 }; 348 + const offlineBtn = { id: "builder-offline", x: pieceBtn.x + pieceBtn.w + 6, y, w: 84, h: 14 }; 349 + const onlineBtn = { id: "builder-online", x: offlineBtn.x + offlineBtn.w + 6, y, w: 76, h: 14 }; 350 + const openBtn = { id: "builder-open", x: onlineBtn.x + onlineBtn.w + 6, y, w: 58, h: 14 }; 351 + 352 + registerRegion(pieceBtn.id, pieceBtn.x, pieceBtn.y, pieceBtn.w, pieceBtn.h, { type: "cycle-piece" }); 353 + registerRegion(offlineBtn.id, offlineBtn.x, offlineBtn.y, offlineBtn.w, offlineBtn.h, { type: "build-offline", piece: customPiece }); 354 + registerRegion(onlineBtn.id, onlineBtn.x, onlineBtn.y, onlineBtn.w, onlineBtn.h, { type: "build-online", piece: customPiece }); 355 + registerRegion(openBtn.id, openBtn.x, openBtn.y, openBtn.w, openBtn.h, { type: "open-piece", piece: customPiece }); 356 + 357 + drawButton({ ink, box }, pieceBtn, currentPieceLabel, btn.warm, hoverId === pieceBtn.id); 358 + drawButton({ ink, box }, offlineBtn, "offline", btn.primary, hoverId === offlineBtn.id); 359 + drawButton({ ink, box }, onlineBtn, "online", btn.alt, hoverId === onlineBtn.id); 360 + drawButton({ ink, box }, openBtn, "open", btn.warm, hoverId === openBtn.id); 361 + 362 + y += 20; 363 + if (customBusy) { 364 + ink(...accent).write(customBusy, { x: margin, y }); 365 + y += 11; 366 + } else if (customMessage) { 367 + ink(dim).write(customMessage, { x: margin, y }); 368 + y += 11; 369 + } else { 370 + y += 2; 371 + } 372 + 373 + y += 8; 374 + 113 375 if (loading) { 114 - // Animated loading dots 115 376 const dots = ".".repeat((floor(frame / 15) % 3) + 1); 116 - ink(dim).write("Loading" + dots, { x: margin, y }); 377 + ink(dim).write("Loading published devices" + dots, { x: margin, y }); 117 378 return; 118 379 } 119 380 ··· 123 384 } 124 385 125 386 if (plugins.length === 0) { 126 - ink(dim).write("No plugins available", { x: margin, y }); 387 + ink(dim).write("No published devices yet.", { x: margin, y }); 127 388 return; 128 389 } 129 390 130 - downloadButtons = []; 391 + ink(...accent).write("Published Devices", { x: margin, y }); 392 + y += 14; 131 393 132 394 for (let i = 0; i < plugins.length; i++) { 133 395 const plugin = plugins[i]; 134 - const cat = plugin.device.category; 135 - const name = stripEmoji(plugin.device.displayName); 396 + const name = stripEmoji(plugin.device?.displayName || plugin.device?.name || plugin.code); 397 + const category = plugin.device?.category || "instrument"; 398 + const piece = plugin.metadata?.piece || plugin.device?.name || "notepat"; 399 + const desc = stripEmoji(plugin.metadata?.description || ""); 400 + const version = plugin.version?.string || "0.0.0"; 401 + const borderColor = category === "midi" 402 + ? [120, 150, 255] 403 + : category === "effect" 404 + ? [255, 170, 90] 405 + : [90, 210, 255]; 406 + const cardBg = dark ? [22, 22, 28] : [248, 248, 244]; 407 + 408 + ink(...cardBg).box(margin - 6, y - 4, cardWidth + 12, 58); 409 + ink(...borderColor, 120).box(margin - 6, y - 4, cardWidth + 12, 58, "outline"); 136 410 137 - // Animated icon by category 138 - if (cat === "midi") iconMidi(ink, box, margin, y, frame + i * 40); 139 - else if (cat === "effect") iconEffect(ink, circle, line, margin, y, frame + i * 40); 411 + if (category === "midi") iconMidi(ink, margin, y, frame + i * 40); 412 + else if (category === "effect") iconEffect(ink, circle, line, margin, y, frame + i * 40); 140 413 else iconInstrument(ink, plot, margin, y, frame + i * 40); 141 414 142 - ink(fg).write(name, { x: margin + 12, y }); 415 + ink(fg).write(name, { x: margin + 14, y }); 143 416 y += 12; 144 417 145 - // Version + category with accent 146 - ink(...accent).write(`v${plugin.version.string} - ${cat}`, { 147 - x: margin, 148 - y, 149 - }); 418 + ink(...accent).write(`v${version} ${category} ${piece}`, { x: margin, y }); 150 419 y += 12; 151 420 152 - // Description 153 - ink(cardDim).write(stripEmoji(plugin.metadata.description || ""), { 154 - x: margin, 155 - y, 156 - }); 157 - y += 14; 421 + ink(dim).write(desc, { x: margin, y }); 158 422 159 - // Download button 160 - const btnW = 108; 161 - const btnH = 14; 162 - const btnX = margin; 163 - const btnY = y; 423 + const btnY = y + 13; 424 + const downloadBtn = { id: `download-${plugin.code}`, x: margin, y: btnY, w: 84, h: 14 }; 425 + const builderBtn = { id: `use-${plugin.code}`, x: margin + 90, y: btnY, w: 68, h: 14 }; 426 + const pieceBtnCard = { id: `open-${plugin.code}`, x: margin + 164, y: btnY, w: 58, h: 14 }; 164 427 165 - const isHovered = 166 - pen && 167 - pen.x >= btnX && 168 - pen.x < btnX + btnW && 169 - pen.y >= btnY && 170 - pen.y < btnY + btnH; 428 + registerRegion(downloadBtn.id, downloadBtn.x, downloadBtn.y, downloadBtn.w, downloadBtn.h, { 429 + type: "download-plugin", 430 + plugin, 431 + }); 432 + registerRegion(builderBtn.id, builderBtn.x, builderBtn.y, builderBtn.w, builderBtn.h, { 433 + type: "use-piece", 434 + piece, 435 + }); 436 + registerRegion(pieceBtnCard.id, pieceBtnCard.x, pieceBtnCard.y, pieceBtnCard.w, pieceBtnCard.h, { 437 + type: "open-piece", 438 + piece, 439 + }); 171 440 172 441 const isDownloading = downloading === plugin.code; 173 - 174 442 if (isDownloading) { 175 - // Animated progress fill 176 - const progress = (frame % 60) / 60; 177 - ink(dark ? [30, 50, 80] : [200, 220, 240]); 178 - box(btnX, btnY, btnW, btnH); 179 - ink(dark ? [60, 160, 80] : [40, 140, 60]); 180 - box(btnX, btnY, floor(btnW * progress), btnH); 181 - ink(255).write("Downloading...", { x: btnX + 4, y: btnY + 3 }); 443 + drawButton({ ink, box }, downloadBtn, "loading", btn.alt, hoverId === downloadBtn.id); 182 444 } else { 183 - // Hover glow 184 - if (isHovered) { 185 - const glow = floor(sin(frame * 0.1) * 15); 186 - ink(btnHover[0] + glow, btnHover[1] + glow, btnHover[2]); 187 - } else { 188 - ink(...btnBg); 189 - } 190 - box(btnX, btnY, btnW, btnH); 191 - ink(255).write("Download .amxd", { x: btnX + 4, y: btnY + 3 }); 445 + drawButton({ ink, box }, downloadBtn, "download", btn.primary, hoverId === downloadBtn.id); 192 446 } 193 - 194 - downloadButtons.push({ 195 - plugin, 196 - bounds: { x: btnX, y: btnY, w: btnW, h: btnH }, 197 - }); 447 + drawButton({ ink, box }, builderBtn, "builder", btn.warm, hoverId === builderBtn.id); 448 + drawButton({ ink, box }, pieceBtnCard, "piece", btn.alt, hoverId === pieceBtnCard.id); 198 449 199 - y += btnH + 14; 200 - 201 - // Separator 450 + y += 35; 202 451 ink(sep).line(margin, y, width - margin, y); 203 452 y += 12; 204 453 } 205 454 } 206 455 207 - function act({ event: e, pen, sound, download }) { 208 - if (!plugins.length) return; 209 - 456 + export async function act({ event: e, pen, sound, download, jump, needsPaint }) { 210 457 if (e.is("move") || e.is("draw")) { 211 - hoveredButton = null; 212 - for (const btn of downloadButtons) { 213 - const { x, y, w, h } = btn.bounds; 214 - if (pen && pen.x >= x && pen.x < x + w && pen.y >= y && pen.y < y + h) { 215 - hoveredButton = btn; 216 - break; 217 - } 458 + const hit = hitRegion(pen); 459 + const nextHoverId = hit?.id || null; 460 + if (nextHoverId !== hoverId) { 461 + hoverId = nextHoverId; 462 + needsPaint?.(); 218 463 } 464 + return; 219 465 } 220 466 221 - if (e.is("touch") && hoveredButton) { 222 - const plugin = hoveredButton.plugin; 223 - sound.synth({ type: "sine", tone: 660, beats: 0.05, volume: 0.2 }); 224 - sound.synth({ type: "sine", tone: 880, beats: 0.05, volume: 0.15, delay: 0.05 }); 467 + if (!e.is("touch")) return; 468 + 469 + const hit = hitRegion(pen); 470 + if (!hit) return; 471 + 472 + const { action } = hit; 473 + 474 + if (action.type === "cycle-piece") { 475 + playClick(sound); 476 + cycleCustomPiece(1); 477 + customMessage = `Builder piece: ${customPiece}`; 478 + needsPaint?.(); 479 + return; 480 + } 225 481 482 + if (action.type === "use-piece") { 483 + playClick(sound); 484 + setCustomPiece(action.piece); 485 + customMessage = `Builder piece: ${customPiece}`; 486 + needsPaint?.(); 487 + return; 488 + } 489 + 490 + if (action.type === "open-piece") { 491 + playClick(sound); 492 + jump?.(action.piece); 493 + return; 494 + } 495 + 496 + if (action.type === "download-plugin") { 497 + const plugin = action.plugin; 498 + playClick(sound); 226 499 downloading = plugin.code; 500 + customMessage = `Downloading ${stripEmoji(plugin.device?.displayName || plugin.code)}...`; 501 + needsPaint?.(); 227 502 228 - const url = plugin.m4l.downloadUrl; 229 - // Use versioned CDN URL if available 230 - const vUrl = url.includes("assets.aesthetic.computer") 231 - ? url 232 - : `https://assets.aesthetic.computer/m4l/${encodeURIComponent(plugin.m4l.fileName)}`; 233 - fetch(vUrl) 234 - .then((res) => res.arrayBuffer()) 235 - .then((buf) => { 236 - download(plugin.m4l.fileName, new Uint8Array(buf), { sharing: true }); 237 - downloading = null; 238 - }) 239 - .catch((err) => { 240 - console.error("Download failed:", err); 241 - downloading = null; 242 - }); 503 + try { 504 + const url = plugin.m4l.downloadUrl; 505 + const assetUrl = url.includes("assets.aesthetic.computer") 506 + ? url 507 + : `https://assets.aesthetic.computer/m4l/${encodeURIComponent(plugin.m4l.fileName)}`; 508 + const res = await fetch(assetUrl); 509 + const buf = await res.arrayBuffer(); 510 + download(plugin.m4l.fileName, new Uint8Array(buf), { sharing: true }); 511 + fetch(`/m4l-plugins/${plugin.code}/download`, { method: "POST" }).catch(() => {}); 512 + customMessage = `Downloaded ${plugin.m4l.fileName}`; 513 + } catch (err) { 514 + console.error("Download failed:", err); 515 + playError(sound); 516 + customMessage = `Download failed: ${err.message}`; 517 + } finally { 518 + downloading = null; 519 + needsPaint?.(); 520 + } 521 + return; 522 + } 243 523 244 - fetch(`/m4l-plugins/${plugin.code}/download`, { method: "POST" }).catch( 245 - () => {}, 246 - ); 524 + if (action.type === "build-offline" || action.type === "build-online") { 525 + if (customBusy) return; 526 + 527 + playClick(sound); 528 + const piece = action.piece; 529 + customBusy = action.type === "build-offline" 530 + ? `Building offline .amxd for ${piece}...` 531 + : `Building online .amxd for ${piece}...`; 532 + customMessage = ""; 533 + needsPaint?.(); 534 + 535 + try { 536 + if (action.type === "build-offline") { 537 + await buildOfflineDevice(piece, download); 538 + customMessage = `Downloaded AC ${piece} (offline).amxd`; 539 + } else { 540 + await buildOnlineDevice(piece, download); 541 + customMessage = `Downloaded AC ${piece} (online).amxd`; 542 + } 543 + } catch (err) { 544 + console.error("Custom Ableton build failed:", err); 545 + playError(sound); 546 + customMessage = `Build failed: ${err.message}`; 547 + } finally { 548 + customBusy = null; 549 + needsPaint?.(); 550 + } 247 551 } 248 552 } 249 553 250 - function meta() { 554 + export function meta() { 251 555 return { 252 556 title: "AC Max for Live", 253 - desc: "Download Aesthetic Computer devices for Ableton Live.", 557 + desc: "Published Ableton devices plus a custom AC instrument builder.", 254 558 }; 255 559 } 256 - 257 - export { paint, act, sim, meta };
+211 -121
system/public/aesthetic.computer/disks/notepat.mjs
··· 1089 1089 1090 1090 // let qrcells; 1091 1091 1092 - let waveBtn, octBtn, osBtn; 1092 + let waveBtn, octBtn, osBtn, abletonBtn; 1093 1093 let slideBtn, roomBtn, glitchBtn, quickBtn; // Toggle buttons for slide/room/glitch/quick modes 1094 1094 let metroBtn, bpmMinusBtn, bpmPlusBtn; // Metronome controls 1095 1095 let melodyAliasBtn; ··· 1667 1667 } 1668 1668 } 1669 1669 1670 - buildWaveButton(api); 1671 - buildOctButton(api); 1672 - buildOsButton(api); 1673 - buildToggleButtons(api); 1674 - buildMetronomeButtons(api); 1670 + buildOctButton(api); 1671 + buildWaveButton(api); 1672 + buildAbletonButton(api); 1673 + buildOsButton(api); 1674 + buildToggleButtons(api); 1675 + buildMetronomeButtons(api); 1675 1676 1676 1677 const newOctave = 1677 1678 parseInt(colon[0]) || parseInt(colon[1]) || parseInt(colon[2]); ··· 4584 4585 4585 4586 updateTheme({ num }); 4586 4587 4587 - if (tap) { 4588 - ink("yellow"); 4589 - write("tap", { right: 6, top: 6 }); 4590 - } else if (!paintPictureOverlay) { 4591 - // OS bar background 4592 - ink(8, 30, 30).box(0, OS_BAR_TOP, screen.width, OS_BAR_HEIGHT); 4593 - ink(5, 20, 20).line(0, OS_BAR_TOP, screen.width, OS_BAR_TOP); 4594 - 4595 - osBtn?.paint((btn) => { 4596 - ink(btn.down ? [20, 70, 70] : [12, 40, 40]).box( 4597 - btn.box.x, 4598 - btn.box.y, 4599 - btn.box.w, 4600 - btn.box.h, 4601 - ); 4602 - if (btn.over && !btn.down) { 4603 - ink(255, 255, 255, 24).box( 4604 - btn.box.x, 4605 - btn.box.y, 4606 - btn.box.w, 4607 - btn.box.h, 4608 - ); 4609 - ink(100, 255, 255, 140).box( 4610 - btn.box.x, 4611 - btn.box.y, 4612 - btn.box.w, 4613 - btn.box.h, 4614 - "outline", 4615 - ); 4616 - } 4617 - ink(70, 160, 160).line( 4618 - btn.box.x + btn.box.w, 4619 - btn.box.y + 1, 4620 - btn.box.x + btn.box.w, 4621 - btn.box.y + btn.box.h - 1, 4622 - ); 4623 - ink(btn.down ? [220, 255, 255] : [120, 255, 255]).write( 4624 - btn.label || "x86 os", 4625 - { x: btn.box.x + 3, y: btn.box.y + 3 }, 4626 - undefined, undefined, false, "MatrixChunky8" 4627 - ); 4628 - }); 4629 - 4630 - waveBtn?.paint((btn) => { 4588 + if (tap) { 4589 + ink("yellow"); 4590 + write("tap", { right: 6, top: 6 }); 4591 + } else if (!paintPictureOverlay) { 4592 + const downloadRailLeft = Math.min( 4593 + osBtn?.box?.x ?? Infinity, 4594 + abletonBtn?.box?.x ?? Infinity, 4595 + ); 4596 + const downloadRailRight = Math.max( 4597 + (osBtn?.box?.x ?? 0) + (osBtn?.box?.w ?? 0), 4598 + (abletonBtn?.box?.x ?? 0) + (abletonBtn?.box?.w ?? 0), 4599 + ); 4600 + if (Number.isFinite(downloadRailLeft) && downloadRailRight > downloadRailLeft) { 4601 + ink(10, 28, 34, 210).box( 4602 + downloadRailLeft - 3, 4603 + 0, 4604 + downloadRailRight - downloadRailLeft + 6, 4605 + TOP_BAR_BOTTOM - 1, 4606 + ); 4607 + ink(55, 120, 135, 170).box( 4608 + downloadRailLeft - 3, 4609 + 0, 4610 + downloadRailRight - downloadRailLeft + 6, 4611 + TOP_BAR_BOTTOM - 1, 4612 + "outline", 4613 + ); 4614 + } 4615 + 4616 + abletonBtn?.paint((btn) => { 4617 + ink(btn.down ? [52, 48, 90] : [30, 28, 62]).box( 4618 + btn.box.x, 4619 + btn.box.y + 3, 4620 + btn.box.w, 4621 + btn.box.h - 3, 4622 + ); 4623 + if (btn.over && !btn.down) { 4624 + ink(255, 255, 255, 24).box( 4625 + btn.box.x, 4626 + btn.box.y + 3, 4627 + btn.box.w, 4628 + btn.box.h - 3, 4629 + ); 4630 + ink(150, 175, 255, 160).box( 4631 + btn.box.x, 4632 + btn.box.y + 3, 4633 + btn.box.w, 4634 + btn.box.h - 3, 4635 + "outline", 4636 + ); 4637 + } 4638 + ink(90, 120, 210).line( 4639 + btn.box.x + btn.box.w, 4640 + btn.box.y + 3, 4641 + btn.box.x + btn.box.w, 4642 + btn.box.y + btn.box.h - 1, 4643 + ); 4644 + ink(btn.down ? [235, 240, 255] : [175, 205, 255]).write( 4645 + "m4l", 4646 + { x: btn.box.x + 3, y: btn.box.y + 5 }, 4647 + undefined, undefined, false, "MatrixChunky8" 4648 + ); 4649 + }); 4650 + 4651 + osBtn?.paint((btn) => { 4652 + ink(btn.down ? [20, 70, 70] : [10, 45, 45]).box( 4653 + btn.box.x, 4654 + btn.box.y + 3, 4655 + btn.box.w, 4656 + btn.box.h - 3, 4657 + ); 4658 + if (btn.over && !btn.down) { 4659 + ink(255, 255, 255, 24).box( 4660 + btn.box.x, 4661 + btn.box.y + 3, 4662 + btn.box.w, 4663 + btn.box.h - 3, 4664 + ); 4665 + ink(100, 255, 255, 140).box( 4666 + btn.box.x, 4667 + btn.box.y + 3, 4668 + btn.box.w, 4669 + btn.box.h - 3, 4670 + "outline", 4671 + ); 4672 + } 4673 + ink(70, 160, 160).line( 4674 + btn.box.x + btn.box.w, 4675 + btn.box.y + 3, 4676 + btn.box.x + btn.box.w, 4677 + btn.box.y + btn.box.h - 1, 4678 + ); 4679 + ink(btn.down ? [220, 255, 255] : [120, 255, 255]).write( 4680 + "os", 4681 + { x: btn.box.x + 3, y: btn.box.y + 5 }, 4682 + undefined, undefined, false, "MatrixChunky8" 4683 + ); 4684 + }); 4685 + 4686 + waveBtn?.paint((btn) => { 4631 4687 ink(btn.down ? [40, 40, 100] : "darkblue").box( 4632 4688 btn.box.x, 4633 4689 btn.box.y + 3, ··· 6031 6087 } 6032 6088 } 6033 6089 if (e.is("reframed")) { 6034 - setupButtons(api); 6035 - buildWaveButton(api); 6036 - buildOctButton(api); 6037 - buildOsButton(api); 6038 - buildToggleButtons(api); 6039 - buildMetronomeButtons(api); 6090 + setupButtons(api); 6091 + buildOctButton(api); 6092 + buildWaveButton(api); 6093 + buildAbletonButton(api); 6094 + buildOsButton(api); 6095 + buildToggleButtons(api); 6096 + buildMetronomeButtons(api); 6040 6097 // Resize picture to quarter resolution (half width, half height) 6041 6098 const resizedPictureWidth = Math.max(1, Math.floor(screen.width / 2)); 6042 6099 const resizedPictureHeight = Math.max(1, Math.floor(screen.height / 2)); ··· 6129 6186 6130 6187 // 🎭 Tap on top bar to toggle recital mode (minimal wireframe UI) 6131 6188 // Only in visualizer area (between piano end and waveBtn), not on piano keys 6132 - if (e.is("touch") && e.y < TOP_BAR_BOTTOM && !projector && !paintPictureOverlay && !recitalMode) { 6133 - // Check that tap is in the visualizer area (after piano, before waveBtn) 6134 - const topBarBase = dotComMode ? 75 : 54; 6135 - const topPianoWidth = Math.min(140, Math.floor((screen.width - topBarBase) * 0.5)); 6136 - const topPianoEndX = topBarBase + topPianoWidth; 6137 - const vizLeft = topPianoEndX; // Start after piano 6138 - const vizRight = (waveBtn?.box?.x ?? screen.width) - 1; 6139 - if (e.x >= vizLeft && e.x <= vizRight) { 6140 - recitalMode = true; 6141 - recitalBlinkPhase = 0; 6142 - } 6143 - } 6189 + if (e.is("touch") && e.y < TOP_BAR_BOTTOM && !projector && !paintPictureOverlay && !recitalMode) { 6190 + // Check that tap is in the visualizer area (after piano, before waveBtn) 6191 + const topBarBase = dotComMode ? 75 : 54; 6192 + const topPianoWidth = Math.min(140, Math.floor((screen.width - topBarBase) * 0.5)); 6193 + const topPianoEndX = topBarBase + topPianoWidth; 6194 + const vizLeft = topPianoEndX; // Start after piano 6195 + const vizRight = Math.min( 6196 + osBtn?.box?.x ?? Infinity, 6197 + abletonBtn?.box?.x ?? Infinity, 6198 + waveBtn?.box?.x ?? screen.width, 6199 + ) - 1; 6200 + if (e.x >= vizLeft && e.x <= vizRight) { 6201 + recitalMode = true; 6202 + recitalBlinkPhase = 0; 6203 + } 6204 + } 6144 6205 6145 6206 if ((e.is("touch") || e.is("lift")) && !paintPictureOverlay && !projector) { 6146 6207 const sampleRateText = getSampleRateText(speaker?.sampleRate); ··· 6285 6346 api.beep(); 6286 6347 waveIndex = (waveIndex + 1) % wavetypes.length; 6287 6348 wave = wavetypes[waveIndex]; 6288 - buildWaveButton(api); 6349 + buildWaveButton(api); 6350 + buildAbletonButton(api); 6351 + buildOsButton(api); 6289 6352 } 6290 6353 6291 6354 // if (e.is("keyboard:down:shift") && !e.repeat) { ··· 7030 7093 cleanupOrphanedSounds(pens); 7031 7094 } 7032 7095 7033 - octBtn?.act(e, { 7034 - down: () => api.beep(400), 7035 - push: (btn) => { 7036 - api.beep(); 7037 - waveIndex = (waveIndex + 1) % wavetypes.length; 7038 - const octNum = parseInt(octave); 7039 - octave = max(1, (octNum + 1) % 10).toString(); 7040 - buildOctButton(api); 7041 - }, 7042 - }); 7096 + octBtn?.act(e, { 7097 + down: () => api.beep(400), 7098 + push: (btn) => { 7099 + api.beep(); 7100 + waveIndex = (waveIndex + 1) % wavetypes.length; 7101 + const octNum = parseInt(octave); 7102 + octave = max(1, (octNum + 1) % 10).toString(); 7103 + buildOctButton(api); 7104 + buildWaveButton(api); 7105 + buildAbletonButton(api); 7106 + buildOsButton(api); 7107 + }, 7108 + }); 7043 7109 7044 - waveBtn?.act(e, { 7045 - down: () => api.beep(400), 7046 - push: (btn) => { 7047 - api.beep(); 7048 - waveIndex = (waveIndex + 1) % wavetypes.length; 7049 - wave = wavetypes[waveIndex]; 7050 - buildWaveButton(api); 7051 - }, 7052 - }); 7053 - 7054 - osBtn?.act(e, { 7055 - down: () => api.beep(400), 7056 - push: () => { 7057 - api.beep(); 7058 - jump("os"); 7059 - }, 7060 - }); 7061 - 7062 - // 🎛️ Toggle button interactions 7110 + waveBtn?.act(e, { 7111 + down: () => api.beep(400), 7112 + push: (btn) => { 7113 + api.beep(); 7114 + waveIndex = (waveIndex + 1) % wavetypes.length; 7115 + wave = wavetypes[waveIndex]; 7116 + buildWaveButton(api); 7117 + buildAbletonButton(api); 7118 + buildOsButton(api); 7119 + }, 7120 + }); 7121 + 7122 + abletonBtn?.act(e, { 7123 + down: () => api.beep(400), 7124 + push: () => { 7125 + api.beep(); 7126 + jump("ableton"); 7127 + }, 7128 + }); 7129 + 7130 + osBtn?.act(e, { 7131 + down: () => api.beep(400), 7132 + push: () => { 7133 + api.beep(); 7134 + jump("os"); 7135 + }, 7136 + }); 7137 + 7138 + // 🎛️ Toggle button interactions 7063 7139 slideBtn?.act(e, { 7064 7140 push: () => { 7065 7141 api.beep(); ··· 8175 8251 waveBtn.displayWave = displayWave; 8176 8252 } 8177 8253 8178 - function buildOctButton({ screen, ui, typeface }) { 8179 - const isNarrow = screen.width < 200; 8180 - const glyphWidth = typeface?.glyphs?.["0"]?.resolution?.[0] ?? matrixFont?.glyphs?.["0"]?.resolution?.[0] ?? 6; 8181 - const octWidth = octave.length * glyphWidth; 8254 + function buildOctButton({ screen, ui, typeface }) { 8255 + const isNarrow = screen.width < 200; 8256 + const glyphWidth = typeface?.glyphs?.["0"]?.resolution?.[0] ?? matrixFont?.glyphs?.["0"]?.resolution?.[0] ?? 6; 8257 + const octWidth = octave.length * glyphWidth; 8182 8258 const margin = isNarrow ? 2 : 4; 8183 8259 octBtn = new ui.Button( 8184 8260 screen.width - octWidth - 6 - margin * 2, ··· 8186 8262 octWidth + margin * 2 + 7, 8187 8263 10 + margin * 2 - 1 + 2, 8188 8264 ); 8189 - octBtn.id = "oct-button"; 8190 - octBtn.isNarrow = isNarrow; 8191 - } 8192 - 8193 - function buildOsButton({ ui, screen }) { 8194 - const label = "x86 os"; 8195 - const margin = 3; 8196 - const labelWidth = label.length * 6; 8197 - const buttonWidth = labelWidth + margin * 2; 8198 - const buttonHeight = OS_BAR_HEIGHT; 8199 - osBtn = new ui.Button( 8200 - 0, 8201 - OS_BAR_TOP, 8202 - buttonWidth, 8203 - buttonHeight, 8204 - ); 8205 - osBtn.id = "os-button"; 8206 - osBtn.label = label; 8207 - } 8265 + octBtn.id = "oct-button"; 8266 + octBtn.isNarrow = isNarrow; 8267 + } 8268 + 8269 + function buildAbletonButton({ ui }) { 8270 + const margin = 4; 8271 + const labelWidth = 3 * 6; 8272 + const buttonWidth = labelWidth + margin * 2; 8273 + const buttonHeight = 10 + margin * 2 - 1 + 2; 8274 + const waveX = waveBtn?.box?.x ?? 9999; 8275 + abletonBtn = new ui.Button( 8276 + waveX - buttonWidth - 3, 8277 + 0, 8278 + buttonWidth, 8279 + buttonHeight, 8280 + ); 8281 + abletonBtn.id = "ableton-button"; 8282 + } 8283 + 8284 + function buildOsButton({ ui }) { 8285 + const margin = 4; 8286 + const labelWidth = 2 * 6; 8287 + const buttonWidth = labelWidth + margin * 2; 8288 + const buttonHeight = 10 + margin * 2 - 1 + 2; 8289 + const abletonX = abletonBtn?.box?.x ?? waveBtn?.box?.x ?? 9999; 8290 + osBtn = new ui.Button( 8291 + abletonX - buttonWidth - 3, 8292 + 0, 8293 + buttonWidth, 8294 + buttonHeight, 8295 + ); 8296 + osBtn.id = "os-button"; 8297 + } 8208 8298 8209 8299 // Build metronome controls and toggle buttons with responsive layout 8210 8300 // Calculates available space and shortens labels as needed to prevent overlap
+360
system/public/aesthetic.computer/disks/spreadnob.mjs
··· 1 + // Spreadnob, 2026.03.31 2 + // AC-native UI for the Ableton spreadnob device. 3 + 4 + import { getNoteColorWithOctave } from "../lib/note-colors.mjs"; 5 + 6 + const FONT = "YWFTProcessing-Regular"; 7 + const MINI_FONT = "MatrixChunky8"; 8 + const KEY_LABELS = ["A", "W", "S", "E", "D", "F", "T", "G", "Y", "H", "U", "J", "K", "O", "L", "P", ";"]; 9 + const PITCH_CLASSES = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]; 10 + const DEFAULT_LOW = 60; 11 + const DEFAULT_HIGH = 76; 12 + const MAX_SPAN = KEY_LABELS.length - 1; 13 + 14 + let active = false; 15 + let target = ""; 16 + let value = null; 17 + let currentNote = null; 18 + let low = DEFAULT_LOW; 19 + let high = DEFAULT_HIGH; 20 + let flash = 0; 21 + let noteHits = 0; 22 + 23 + let uiKit = null; 24 + let requestPaint = () => {}; 25 + let messageHandler = null; 26 + let layoutKey = ""; 27 + 28 + let lowDownBtn = null; 29 + let lowUpBtn = null; 30 + let resetBtn = null; 31 + let highDownBtn = null; 32 + let highUpBtn = null; 33 + 34 + function clamp(n, min, max) { 35 + return Math.max(min, Math.min(max, n)); 36 + } 37 + 38 + function noteLabel(midi) { 39 + if (!Number.isFinite(midi)) return "--"; 40 + const pitch = PITCH_CLASSES[((midi % 12) + 12) % 12].toUpperCase(); 41 + const octave = Math.floor(midi / 12) - 1; 42 + return `${pitch}${octave}`; 43 + } 44 + 45 + function noteColor(midi) { 46 + if (!Number.isFinite(midi)) return [190, 190, 190]; 47 + const pitch = PITCH_CLASSES[((midi % 12) + 12) % 12]; 48 + const octave = Math.floor(midi / 12) - 1; 49 + return getNoteColorWithOctave(pitch, octave); 50 + } 51 + 52 + function isBlack(midi) { 53 + return [1, 3, 6, 8, 10].includes(((midi % 12) + 12) % 12); 54 + } 55 + 56 + function isDefaultRange() { 57 + return low === DEFAULT_LOW && high === DEFAULT_HIGH; 58 + } 59 + 60 + function rangeCount() { 61 + return clamp(high - low + 1, 1, KEY_LABELS.length); 62 + } 63 + 64 + function setRange(nextLow, nextHigh, { emit = false } = {}) { 65 + let resolvedLow = clamp(Math.round(nextLow), 0, 127); 66 + let resolvedHigh = clamp(Math.round(nextHigh), 0, 127); 67 + 68 + if (resolvedHigh < resolvedLow) resolvedHigh = resolvedLow; 69 + 70 + if (resolvedHigh - resolvedLow > MAX_SPAN) { 71 + if (resolvedLow !== low) { 72 + resolvedHigh = resolvedLow + MAX_SPAN; 73 + } else { 74 + resolvedLow = resolvedHigh - MAX_SPAN; 75 + } 76 + } 77 + 78 + low = clamp(resolvedLow, 0, 127); 79 + high = clamp(resolvedHigh, low, 127); 80 + 81 + if (emit && typeof window !== "undefined" && window.max?.outlet) { 82 + try { 83 + window.max.outlet("range", low, high); 84 + } catch {} 85 + } 86 + 87 + requestPaint(); 88 + } 89 + 90 + function updateFromMessage(data = {}) { 91 + if (data.type === "spreadnob:active") { 92 + active = !!Number(data.active); 93 + requestPaint(); 94 + return; 95 + } 96 + 97 + if (data.type === "spreadnob:target") { 98 + target = String(data.name || "").trim(); 99 + requestPaint(); 100 + return; 101 + } 102 + 103 + if (data.type === "spreadnob:value") { 104 + const num = Number(data.value); 105 + value = Number.isFinite(num) ? num : null; 106 + requestPaint(); 107 + return; 108 + } 109 + 110 + if (data.type === "spreadnob:range") { 111 + setRange(Number(data.low), Number(data.high), { emit: false }); 112 + return; 113 + } 114 + 115 + if (data.type === "spreadnob:note") { 116 + const num = Number(data.note); 117 + currentNote = Number.isFinite(num) ? num : null; 118 + noteHits++; 119 + flash = active ? 1 : 0.45; 120 + requestPaint(); 121 + } 122 + } 123 + 124 + function buildUi(screen) { 125 + if (!uiKit) return; 126 + 127 + const key = `${screen.width}x${screen.height}`; 128 + if (layoutKey === key) return; 129 + layoutKey = key; 130 + 131 + const footerY = screen.height - 18; 132 + const btnY = footerY + 2; 133 + const sideX = 8; 134 + const btnW = 16; 135 + const btnH = 13; 136 + const chipW = 48; 137 + const rightX = screen.width - sideX - btnW; 138 + const resetW = 56; 139 + const resetX = Math.round((screen.width - resetW) / 2); 140 + 141 + lowDownBtn = new uiKit.Button(sideX, btnY, btnW, btnH); 142 + lowUpBtn = new uiKit.Button(sideX + btnW + 2, btnY, btnW, btnH); 143 + resetBtn = new uiKit.Button(resetX, btnY, resetW, btnH); 144 + highDownBtn = new uiKit.Button(rightX - btnW - 2, btnY, btnW, btnH); 145 + highUpBtn = new uiKit.Button(rightX, btnY, btnW, btnH); 146 + 147 + lowDownBtn.labelBox = { x: sideX, y: footerY - 11, w: chipW, h: 10 }; 148 + highUpBtn.labelBox = { x: screen.width - sideX - chipW, y: footerY - 11, w: chipW, h: 10 }; 149 + } 150 + 151 + function boot({ ui, screen, needsPaint }) { 152 + uiKit = ui; 153 + requestPaint = needsPaint; 154 + buildUi(screen); 155 + 156 + if (typeof window !== "undefined") { 157 + messageHandler = (event) => updateFromMessage(event?.data || {}); 158 + window.addEventListener("message", messageHandler); 159 + 160 + window.acSpreadnobSetActive = (next) => 161 + updateFromMessage({ type: "spreadnob:active", active: next }); 162 + window.acSpreadnobSetTarget = (name) => 163 + updateFromMessage({ type: "spreadnob:target", name }); 164 + window.acSpreadnobSetValue = (next) => 165 + updateFromMessage({ type: "spreadnob:value", value: next }); 166 + window.acSpreadnobSetRange = (nextLow, nextHigh) => 167 + updateFromMessage({ type: "spreadnob:range", low: nextLow, high: nextHigh }); 168 + window.acSpreadnobNote = (note) => 169 + updateFromMessage({ type: "spreadnob:note", note }); 170 + } 171 + 172 + requestPaint(); 173 + } 174 + 175 + function sim({ needsPaint }) { 176 + if (flash <= 0) return; 177 + flash *= 0.82; 178 + if (flash < 0.025) flash = 0; 179 + needsPaint(); 180 + } 181 + 182 + function paintButton({ button, label, ink, box, fg = [245, 240, 250], bg = [52, 26, 41] }) { 183 + button?.paint((btn) => { 184 + ink(...bg).box(btn.box); 185 + ink(255, 110, 180, 80).box(btn.box.x, btn.box.y, btn.box.w, 1); 186 + ink(...fg).write(label, { x: btn.box.x + 5, y: btn.box.y + 1 }, undefined, undefined, false, FONT); 187 + }); 188 + } 189 + 190 + function paint({ wipe, ink, screen, line }) { 191 + buildUi(screen); 192 + 193 + const w = screen.width; 194 + const h = screen.height; 195 + const targetName = target || "CLICK ABLETON KNOB"; 196 + const activeFill = active ? [110, 255, 120] : [255, 90, 95]; 197 + const shell = active ? [19, 33, 20] : [36, 17, 24]; 198 + const border = active ? [60, 180, 90] : [170, 50, 85]; 199 + const flashAlpha = Math.round(110 * flash); 200 + const keyboardY = 97; 201 + const keyboardX = 10; 202 + const keyboardW = w - 20; 203 + const keyboardH = 46; 204 + const count = rangeCount(); 205 + const gap = 2; 206 + const keyW = Math.max(8, Math.floor((keyboardW - gap * (KEY_LABELS.length - 1)) / KEY_LABELS.length)); 207 + const totalKeyW = keyW * KEY_LABELS.length + gap * (KEY_LABELS.length - 1); 208 + const keyStartX = Math.round((w - totalKeyW) / 2); 209 + 210 + wipe(8, 10, 12); 211 + ink(...shell).box(0, 0, w, h); 212 + ink(255, 80, 150, 28).box(0, 0, w, 28); 213 + ink(40, 120, 80, active ? 44 : 18).box(0, 24, w, h - 24); 214 + 215 + if (flashAlpha > 0) { 216 + ink(active ? 110 : 255, active ? 255 : 105, active ? 150 : 145, flashAlpha).box(0, 0, w, h); 217 + } 218 + 219 + ink(...border).box(6, 6, w - 12, h - 12); 220 + ink(255, 115, 180, 120).line(10, 34, w - 10, 34); 221 + ink(255, 115, 180, 60).line(10, 88, w - 10, 88); 222 + ink(255, 115, 180, 60).line(10, 149, w - 10, 149); 223 + 224 + ink(255, 120, 185).write("spreadnob", { x: 13, y: 10 }, undefined, undefined, false, FONT); 225 + 226 + ink(...activeFill).box(w - 64, 10, 50, 14); 227 + ink(10, 18, 12).write(active ? "ON" : "OFF", { x: w - 49, y: 11 }, undefined, undefined, false, FONT); 228 + 229 + ink(255, 190, 220).write(targetName, { x: 13, y: 42 }, undefined, w - 26, true, FONT); 230 + ink(170, 145, 170).write( 231 + active ? "follows selected knob" : "device bypassed by ableton", 232 + { x: 13, y: 58 }, 233 + undefined, 234 + undefined, 235 + false, 236 + MINI_FONT, 237 + ); 238 + 239 + const statY = 67; 240 + const statW = Math.floor((w - 34) / 3); 241 + const statXs = [13, 13 + statW + 4, 13 + (statW + 4) * 2]; 242 + const valueText = value === null ? "--" : value.toFixed(3); 243 + const noteText = currentNote === null ? "--" : `${noteLabel(currentNote)} ${currentNote}`; 244 + const rangeText = `${low}..${high}`; 245 + 246 + for (const x of statXs) { 247 + ink(24, 18, 28).box(x, statY, statW, 17); 248 + ink(255, 255, 255, 24).box(x, statY, statW, 1); 249 + } 250 + 251 + ink(150, 135, 150).write("value", { x: statXs[0] + 3, y: statY + 2 }, undefined, undefined, false, MINI_FONT); 252 + ink(255, 118, 180).write(valueText, { x: statXs[0] + 3, y: statY + 8 }, undefined, statW - 6, true, FONT); 253 + 254 + ink(150, 135, 150).write("note", { x: statXs[1] + 3, y: statY + 2 }, undefined, undefined, false, MINI_FONT); 255 + ink(130, 230, 255).write(noteText, { x: statXs[1] + 3, y: statY + 8 }, undefined, statW - 6, true, FONT); 256 + 257 + ink(150, 135, 150).write("range", { x: statXs[2] + 3, y: statY + 2 }, undefined, undefined, false, MINI_FONT); 258 + ink(210, 245, 160).write(rangeText, { x: statXs[2] + 3, y: statY + 8 }, undefined, statW - 6, true, FONT); 259 + 260 + ink(255, 135, 200).write( 261 + isDefaultRange() ? "ableton qwerty spread" : "custom key spread", 262 + { x: 13, y: 92 }, 263 + undefined, 264 + undefined, 265 + false, 266 + MINI_FONT, 267 + ); 268 + 269 + ink(14, 14, 18).box(keyboardX, keyboardY, keyboardW, keyboardH); 270 + 271 + for (let i = 0; i < KEY_LABELS.length; i++) { 272 + const x = keyStartX + i * (keyW + gap); 273 + const inRange = i < count; 274 + const midi = low + i; 275 + const current = currentNote === midi; 276 + const black = inRange && isBlack(midi); 277 + const baseY = keyboardY + (black ? 4 : 0); 278 + const keyH = black ? keyboardH - 14 : keyboardH; 279 + const rgb = inRange ? noteColor(midi) : [42, 42, 46]; 280 + const fill = current 281 + ? [255, 105, 182] 282 + : black 283 + ? [rgb[0] * 0.45, rgb[1] * 0.45, rgb[2] * 0.45] 284 + : [rgb[0] * 0.7, rgb[1] * 0.7, rgb[2] * 0.7]; 285 + 286 + ink(...fill).box(x, baseY, keyW, keyH); 287 + ink(current ? 255 : 255, current ? 240 : 255, current ? 245 : 255, current ? 150 : 24).box(x, baseY, keyW, 1); 288 + 289 + if (inRange) { 290 + ink(250, 248, 252).write(KEY_LABELS[i], { x: x + 2, y: keyboardY + keyboardH - 11 }, undefined, undefined, false, MINI_FONT); 291 + ink(black ? 255 : 25, black ? 190 : 34, black ? 220 : 42).write(String(midi), { x: x + 1, y: baseY + 1 }, undefined, undefined, false, MINI_FONT); 292 + if (current) { 293 + ink(255, 245, 250).line(x, baseY + keyH, x + keyW, baseY + keyH); 294 + } 295 + } 296 + } 297 + 298 + ink(160, 145, 165).write( 299 + currentNote === null ? "press a note to see the map move" : `${noteLabel(currentNote)} hit ${noteHits}`, 300 + { x: 12, y: 152 }, 301 + undefined, 302 + w - 24, 303 + true, 304 + MINI_FONT, 305 + ); 306 + 307 + ink(110, 230, 170).write(`${low} ${noteLabel(low)}`, { x: 8, y: h - 28 }, undefined, undefined, false, MINI_FONT); 308 + ink(110, 230, 170).write(`${high} ${noteLabel(high)}`, { right: 8, y: h - 28 }, undefined, undefined, false, MINI_FONT); 309 + 310 + paintButton({ button: lowDownBtn, label: "-", ink }); 311 + paintButton({ button: lowUpBtn, label: "+", ink }); 312 + paintButton({ 313 + button: resetBtn, 314 + label: isDefaultRange() ? "QWERTY" : "RESET", 315 + ink, 316 + fg: isDefaultRange() ? [230, 235, 240] : [220, 255, 180], 317 + bg: isDefaultRange() ? [40, 45, 50] : [34, 56, 30], 318 + }); 319 + paintButton({ button: highDownBtn, label: "-", ink }); 320 + paintButton({ button: highUpBtn, label: "+", ink }); 321 + } 322 + 323 + function act({ event, screen, needsPaint }) { 324 + buildUi(screen); 325 + 326 + lowDownBtn?.act(event, () => setRange(low - 1, high, { emit: true })); 327 + lowUpBtn?.act(event, () => setRange(low + 1, high, { emit: true })); 328 + highDownBtn?.act(event, () => setRange(low, high - 1, { emit: true })); 329 + highUpBtn?.act(event, () => setRange(low, high + 1, { emit: true })); 330 + resetBtn?.act(event, () => setRange(DEFAULT_LOW, DEFAULT_HIGH, { emit: true })); 331 + 332 + if (event.is("keyboard:down:left")) setRange(low - 1, high, { emit: true }); 333 + if (event.is("keyboard:down:right")) setRange(low, high + 1, { emit: true }); 334 + if (event.is("keyboard:down:down")) setRange(low + 1, high, { emit: true }); 335 + if (event.is("keyboard:down:up")) setRange(low, high - 1, { emit: true }); 336 + if (event.is("keyboard:down:r")) setRange(DEFAULT_LOW, DEFAULT_HIGH, { emit: true }); 337 + 338 + needsPaint(); 339 + } 340 + 341 + function leave() { 342 + if (typeof window !== "undefined") { 343 + if (messageHandler) window.removeEventListener("message", messageHandler); 344 + delete window.acSpreadnobSetActive; 345 + delete window.acSpreadnobSetTarget; 346 + delete window.acSpreadnobSetValue; 347 + delete window.acSpreadnobSetRange; 348 + delete window.acSpreadnobNote; 349 + } 350 + } 351 + 352 + function meta() { 353 + return { 354 + title: "Spreadnob", 355 + desc: "AC-native control surface for the Ableton spreadnob device.", 356 + }; 357 + } 358 + 359 + export const nohud = true; 360 + export { boot, sim, paint, act, leave, meta };