├── README.txt ├── humanize Project ├── Ableton Project Info │ └── Project8_1.cfg ├── Icon └── humanize.als ├── humanize.amxd └── humanize.js /README.txt: -------------------------------------------------------------------------------- 1 | This is an example of using the Max for Live JavaScript API to modify MIDI clips. 2 | 3 | It provides "humanize" features to randomize the start time and velocity of notes. 4 | 5 | A step-by-step tutorial at http://compusition.com/writings/js-live-api explains how this project was built. 6 | -------------------------------------------------------------------------------- /humanize Project/Ableton Project Info/Project8_1.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamjmurray/js-live-api-humanize-midi-clips/40d037a93afecc0fa427b38567a20f6a873f3432/humanize Project/Ableton Project Info/Project8_1.cfg -------------------------------------------------------------------------------- /humanize Project/Icon : -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamjmurray/js-live-api-humanize-midi-clips/40d037a93afecc0fa427b38567a20f6a873f3432/humanize Project/Icon -------------------------------------------------------------------------------- /humanize Project/humanize.als: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamjmurray/js-live-api-humanize-midi-clips/40d037a93afecc0fa427b38567a20f6a873f3432/humanize Project/humanize.als -------------------------------------------------------------------------------- /humanize.amxd: -------------------------------------------------------------------------------- 1 | ampfmmmmmetaptch 8{ 2 | "patcher" : { 3 | "fileversion" : 1, 4 | "appversion" : { 5 | "major" : 6, 6 | "minor" : 1, 7 | "revision" : 7, 8 | "architecture" : "x64" 9 | } 10 | , 11 | "rect" : [ 119.0, 613.0, 454.0, 499.0 ], 12 | "openrect" : [ 0.0, 0.0, 0.0, 169.0 ], 13 | "bglocked" : 0, 14 | "openinpresentation" : 0, 15 | "default_fontsize" : 10.0, 16 | "default_fontface" : 0, 17 | "default_fontname" : "Arial Bold", 18 | "gridonopen" : 0, 19 | "gridsize" : [ 8.0, 8.0 ], 20 | "gridsnaponopen" : 0, 21 | "statusbarvisible" : 0, 22 | "toolbarvisible" : 1, 23 | "boxanimatetime" : 500, 24 | "imprint" : 0, 25 | "enablehscroll" : 1, 26 | "enablevscroll" : 1, 27 | "devicewidth" : 0.0, 28 | "description" : "", 29 | "digest" : "", 30 | "tags" : "", 31 | "boxes" : [ { 32 | "box" : { 33 | "id" : "obj-5", 34 | "maxclass" : "live.text", 35 | "mode" : 0, 36 | "numinlets" : 1, 37 | "numoutlets" : 2, 38 | "outlettype" : [ "", "" ], 39 | "parameter_enable" : 1, 40 | "patching_rect" : [ 11.0, 67.0, 136.0, 25.0 ], 41 | "saved_attribute_attributes" : { 42 | "valueof" : { 43 | "parameter_longname" : "live.text[2]", 44 | "parameter_shortname" : "live.text[2]", 45 | "parameter_type" : 2, 46 | "parameter_mmax" : 1.0, 47 | "parameter_enum" : [ "val1", "val2" ] 48 | } 49 | 50 | } 51 | , 52 | "text" : "Open Humanize Controls", 53 | "varname" : "live.text" 54 | } 55 | 56 | } 57 | , { 58 | "box" : { 59 | "fontname" : "Arial Bold", 60 | "fontsize" : 10.0, 61 | "hidden" : 1, 62 | "id" : "obj-9", 63 | "maxclass" : "newobj", 64 | "numinlets" : 1, 65 | "numoutlets" : 4, 66 | "outlettype" : [ "", "", "", "" ], 67 | "patcher" : { 68 | "fileversion" : 1, 69 | "appversion" : { 70 | "major" : 6, 71 | "minor" : 1, 72 | "revision" : 7, 73 | "architecture" : "x64" 74 | } 75 | , 76 | "rect" : [ 241.0, 122.0, 259.0, 150.0 ], 77 | "bglocked" : 0, 78 | "openinpresentation" : 0, 79 | "default_fontsize" : 12.0, 80 | "default_fontface" : 0, 81 | "default_fontname" : "Arial", 82 | "gridonopen" : 0, 83 | "gridsize" : [ 15.0, 15.0 ], 84 | "gridsnaponopen" : 0, 85 | "statusbarvisible" : 2, 86 | "toolbarvisible" : 1, 87 | "boxanimatetime" : 200, 88 | "imprint" : 0, 89 | "enablehscroll" : 1, 90 | "enablevscroll" : 1, 91 | "devicewidth" : 0.0, 92 | "description" : "", 93 | "digest" : "", 94 | "tags" : "", 95 | "boxes" : [ { 96 | "box" : { 97 | "fontname" : "Arial", 98 | "fontsize" : 12.0, 99 | "frgb" : 0.0, 100 | "id" : "obj-12", 101 | "maxclass" : "comment", 102 | "numinlets" : 1, 103 | "numoutlets" : 0, 104 | "patching_rect" : [ 159.0, 11.0, 69.0, 20.0 ], 105 | "text" : "max delta" 106 | } 107 | 108 | } 109 | , { 110 | "box" : { 111 | "fontname" : "Arial", 112 | "fontsize" : 12.0, 113 | "frgb" : 0.0, 114 | "id" : "obj-10", 115 | "maxclass" : "comment", 116 | "numinlets" : 1, 117 | "numoutlets" : 0, 118 | "patching_rect" : [ 19.0, 11.0, 68.0, 20.0 ], 119 | "text" : "humanize" 120 | } 121 | 122 | } 123 | , { 124 | "box" : { 125 | "fontname" : "Arial", 126 | "fontsize" : 12.0, 127 | "hidden" : 1, 128 | "id" : "obj-4", 129 | "maxclass" : "newobj", 130 | "numinlets" : 1, 131 | "numoutlets" : 2, 132 | "outlettype" : [ "", "" ], 133 | "patching_rect" : [ 20.0, 345.0, 69.0, 20.0 ], 134 | "save" : [ "#N", "thispatcher", ";", "#Q", "end", ";" ], 135 | "text" : "thispatcher" 136 | } 137 | 138 | } 139 | , { 140 | "box" : { 141 | "fontname" : "Arial", 142 | "fontsize" : 12.0, 143 | "hidden" : 1, 144 | "id" : "obj-3", 145 | "maxclass" : "message", 146 | "numinlets" : 2, 147 | "numoutlets" : 1, 148 | "outlettype" : [ "" ], 149 | "patching_rect" : [ 20.0, 307.0, 211.0, 18.0 ], 150 | "text" : "window flags float, window exec, front" 151 | } 152 | 153 | } 154 | , { 155 | "box" : { 156 | "comment" : "", 157 | "hidden" : 1, 158 | "id" : "obj-1", 159 | "maxclass" : "inlet", 160 | "numinlets" : 0, 161 | "numoutlets" : 1, 162 | "outlettype" : [ "" ], 163 | "patching_rect" : [ 20.0, 262.0, 25.0, 25.0 ] 164 | } 165 | 166 | } 167 | , { 168 | "box" : { 169 | "id" : "obj-14", 170 | "maxclass" : "live.text", 171 | "mode" : 0, 172 | "numinlets" : 1, 173 | "numoutlets" : 2, 174 | "outlettype" : [ "", "" ], 175 | "parameter_enable" : 1, 176 | "patching_rect" : [ 21.0, 68.0, 58.0, 20.0 ], 177 | "saved_attribute_attributes" : { 178 | "valueof" : { 179 | "parameter_longname" : "live.text[1]", 180 | "parameter_shortname" : "live.text", 181 | "parameter_type" : 2, 182 | "parameter_mmax" : 1.0, 183 | "parameter_enum" : [ "val1", "val2" ] 184 | } 185 | 186 | } 187 | , 188 | "text" : "velocity", 189 | "varname" : "live.text[1]" 190 | } 191 | 192 | } 193 | , { 194 | "box" : { 195 | "id" : "obj-16", 196 | "maxclass" : "live.slider", 197 | "numinlets" : 1, 198 | "numoutlets" : 2, 199 | "outlettype" : [ "", "float" ], 200 | "parameter_enable" : 1, 201 | "patching_rect" : [ 189.0, 38.0, 39.0, 95.0 ], 202 | "saved_attribute_attributes" : { 203 | "valueof" : { 204 | "parameter_longname" : "live.slider[1]", 205 | "parameter_shortname" : "velocity", 206 | "parameter_type" : 1, 207 | "parameter_mmin" : 1.0, 208 | "parameter_unitstyle" : 0 209 | } 210 | 211 | } 212 | , 213 | "varname" : "live.slider[1]" 214 | } 215 | 216 | } 217 | , { 218 | "box" : { 219 | "id" : "obj-13", 220 | "maxclass" : "live.text", 221 | "mode" : 0, 222 | "numinlets" : 1, 223 | "numoutlets" : 2, 224 | "outlettype" : [ "", "" ], 225 | "parameter_enable" : 1, 226 | "patching_rect" : [ 30.0, 38.0, 40.0, 20.0 ], 227 | "saved_attribute_attributes" : { 228 | "valueof" : { 229 | "parameter_longname" : "live.text", 230 | "parameter_shortname" : "live.text", 231 | "parameter_type" : 2, 232 | "parameter_mmax" : 1.0, 233 | "parameter_enum" : [ "val1", "val2" ] 234 | } 235 | 236 | } 237 | , 238 | "text" : "time", 239 | "varname" : "live.text" 240 | } 241 | 242 | } 243 | , { 244 | "box" : { 245 | "id" : "obj-9", 246 | "maxclass" : "live.slider", 247 | "numinlets" : 1, 248 | "numoutlets" : 2, 249 | "outlettype" : [ "", "float" ], 250 | "parameter_enable" : 1, 251 | "patching_rect" : [ 144.0, 38.0, 39.0, 95.0 ], 252 | "saved_attribute_attributes" : { 253 | "valueof" : { 254 | "parameter_longname" : "live.slider", 255 | "parameter_shortname" : "time", 256 | "parameter_type" : 0, 257 | "parameter_mmin" : 0.01, 258 | "parameter_mmax" : 0.25, 259 | "parameter_unitstyle" : 1 260 | } 261 | 262 | } 263 | , 264 | "varname" : "live.slider" 265 | } 266 | 267 | } 268 | , { 269 | "box" : { 270 | "comment" : "", 271 | "hidden" : 1, 272 | "id" : "obj-5", 273 | "maxclass" : "outlet", 274 | "numinlets" : 1, 275 | "numoutlets" : 0, 276 | "patching_rect" : [ 20.0, 183.0, 25.0, 25.0 ] 277 | } 278 | 279 | } 280 | , { 281 | "box" : { 282 | "comment" : "", 283 | "hidden" : 1, 284 | "id" : "obj-6", 285 | "maxclass" : "outlet", 286 | "numinlets" : 1, 287 | "numoutlets" : 0, 288 | "patching_rect" : [ 59.0, 183.0, 25.0, 25.0 ] 289 | } 290 | 291 | } 292 | , { 293 | "box" : { 294 | "comment" : "", 295 | "hidden" : 1, 296 | "id" : "obj-7", 297 | "maxclass" : "outlet", 298 | "numinlets" : 1, 299 | "numoutlets" : 0, 300 | "patching_rect" : [ 112.0, 183.0, 25.0, 25.0 ] 301 | } 302 | 303 | } 304 | , { 305 | "box" : { 306 | "comment" : "", 307 | "hidden" : 1, 308 | "id" : "obj-8", 309 | "maxclass" : "outlet", 310 | "numinlets" : 1, 311 | "numoutlets" : 0, 312 | "patching_rect" : [ 158.0, 183.0, 25.0, 25.0 ] 313 | } 314 | 315 | } 316 | ], 317 | "lines" : [ { 318 | "patchline" : { 319 | "destination" : [ "obj-3", 0 ], 320 | "disabled" : 0, 321 | "hidden" : 1, 322 | "source" : [ "obj-1", 0 ] 323 | } 324 | 325 | } 326 | , { 327 | "patchline" : { 328 | "destination" : [ "obj-5", 0 ], 329 | "disabled" : 0, 330 | "hidden" : 1, 331 | "source" : [ "obj-13", 0 ] 332 | } 333 | 334 | } 335 | , { 336 | "patchline" : { 337 | "destination" : [ "obj-7", 0 ], 338 | "disabled" : 0, 339 | "hidden" : 1, 340 | "source" : [ "obj-14", 0 ] 341 | } 342 | 343 | } 344 | , { 345 | "patchline" : { 346 | "destination" : [ "obj-8", 0 ], 347 | "disabled" : 0, 348 | "hidden" : 1, 349 | "source" : [ "obj-16", 0 ] 350 | } 351 | 352 | } 353 | , { 354 | "patchline" : { 355 | "destination" : [ "obj-4", 0 ], 356 | "disabled" : 0, 357 | "hidden" : 1, 358 | "source" : [ "obj-3", 0 ] 359 | } 360 | 361 | } 362 | , { 363 | "patchline" : { 364 | "destination" : [ "obj-6", 0 ], 365 | "disabled" : 0, 366 | "hidden" : 1, 367 | "source" : [ "obj-9", 0 ] 368 | } 369 | 370 | } 371 | ] 372 | } 373 | , 374 | "patching_rect" : [ 29.0, 195.0, 66.0, 18.0 ], 375 | "saved_object_attributes" : { 376 | "default_fontface" : 0, 377 | "default_fontname" : "Arial", 378 | "default_fontsize" : 12.0, 379 | "description" : "", 380 | "digest" : "", 381 | "fontface" : 0, 382 | "fontname" : "Arial", 383 | "fontsize" : 12.0, 384 | "globalpatchername" : "", 385 | "tags" : "" 386 | } 387 | , 388 | "text" : "p humanize" 389 | } 390 | 391 | } 392 | , { 393 | "box" : { 394 | "fontname" : "Arial Bold", 395 | "fontsize" : 10.0, 396 | "hidden" : 1, 397 | "id" : "obj-15", 398 | "linecount" : 2, 399 | "maxclass" : "message", 400 | "numinlets" : 2, 401 | "numoutlets" : 1, 402 | "outlettype" : [ "" ], 403 | "patching_rect" : [ 213.0, 355.0, 92.0, 27.0 ], 404 | "text" : "humanize velocity 0 15" 405 | } 406 | 407 | } 408 | , { 409 | "box" : { 410 | "fontname" : "Arial Bold", 411 | "fontsize" : 10.0, 412 | "hidden" : 1, 413 | "id" : "obj-17", 414 | "maxclass" : "newobj", 415 | "numinlets" : 1, 416 | "numoutlets" : 1, 417 | "outlettype" : [ "" ], 418 | "patching_rect" : [ 286.0, 321.0, 147.0, 18.0 ], 419 | "text" : "prepend humanize velocity 0" 420 | } 421 | 422 | } 423 | , { 424 | "box" : { 425 | "fontname" : "Arial Bold", 426 | "fontsize" : 10.0, 427 | "hidden" : 1, 428 | "id" : "obj-11", 429 | "linecount" : 3, 430 | "maxclass" : "message", 431 | "numinlets" : 2, 432 | "numoutlets" : 1, 433 | "outlettype" : [ "" ], 434 | "patching_rect" : [ 30.0, 355.0, 58.0, 38.0 ], 435 | "text" : "humanize time 0.094714" 436 | } 437 | 438 | } 439 | , { 440 | "box" : { 441 | "fontname" : "Arial Bold", 442 | "fontsize" : 10.0, 443 | "hidden" : 1, 444 | "id" : "obj-4", 445 | "maxclass" : "newobj", 446 | "numinlets" : 1, 447 | "numoutlets" : 1, 448 | "outlettype" : [ "" ], 449 | "patching_rect" : [ 69.0, 321.0, 122.0, 18.0 ], 450 | "text" : "prepend humanize time" 451 | } 452 | 453 | } 454 | , { 455 | "box" : { 456 | "fontname" : "Arial Bold", 457 | "fontsize" : 10.0, 458 | "hidden" : 1, 459 | "id" : "obj-3", 460 | "maxclass" : "newobj", 461 | "numinlets" : 1, 462 | "numoutlets" : 1, 463 | "outlettype" : [ "" ], 464 | "patching_rect" : [ 112.0, 409.0, 79.0, 18.0 ], 465 | "saved_object_attributes" : { 466 | "filename" : "humanize.js", 467 | "parameter_enable" : 0 468 | } 469 | , 470 | "text" : "js humanize.js" 471 | } 472 | 473 | } 474 | , { 475 | "box" : { 476 | "fontname" : "Arial Bold", 477 | "fontsize" : 10.0, 478 | "hidden" : 1, 479 | "id" : "obj-2", 480 | "maxclass" : "newobj", 481 | "numinlets" : 1, 482 | "numoutlets" : 0, 483 | "patching_rect" : [ 30.0, 465.0, 47.0, 18.0 ], 484 | "text" : "midiout" 485 | } 486 | 487 | } 488 | , { 489 | "box" : { 490 | "fontname" : "Arial Bold", 491 | "fontsize" : 10.0, 492 | "hidden" : 1, 493 | "id" : "obj-1", 494 | "maxclass" : "newobj", 495 | "numinlets" : 1, 496 | "numoutlets" : 1, 497 | "outlettype" : [ "int" ], 498 | "patching_rect" : [ 30.0, 432.0, 40.0, 18.0 ], 499 | "text" : "midiin" 500 | } 501 | 502 | } 503 | ], 504 | "lines" : [ { 505 | "patchline" : { 506 | "destination" : [ "obj-2", 0 ], 507 | "disabled" : 0, 508 | "hidden" : 1, 509 | "source" : [ "obj-1", 0 ] 510 | } 511 | 512 | } 513 | , { 514 | "patchline" : { 515 | "destination" : [ "obj-3", 0 ], 516 | "disabled" : 0, 517 | "hidden" : 1, 518 | "source" : [ "obj-11", 0 ] 519 | } 520 | 521 | } 522 | , { 523 | "patchline" : { 524 | "destination" : [ "obj-3", 0 ], 525 | "disabled" : 0, 526 | "hidden" : 1, 527 | "source" : [ "obj-15", 0 ] 528 | } 529 | 530 | } 531 | , { 532 | "patchline" : { 533 | "destination" : [ "obj-15", 1 ], 534 | "disabled" : 0, 535 | "hidden" : 1, 536 | "source" : [ "obj-17", 0 ] 537 | } 538 | 539 | } 540 | , { 541 | "patchline" : { 542 | "destination" : [ "obj-11", 1 ], 543 | "disabled" : 0, 544 | "hidden" : 1, 545 | "source" : [ "obj-4", 0 ] 546 | } 547 | 548 | } 549 | , { 550 | "patchline" : { 551 | "destination" : [ "obj-9", 0 ], 552 | "disabled" : 0, 553 | "hidden" : 1, 554 | "source" : [ "obj-5", 0 ] 555 | } 556 | 557 | } 558 | , { 559 | "patchline" : { 560 | "destination" : [ "obj-11", 0 ], 561 | "disabled" : 0, 562 | "hidden" : 1, 563 | "source" : [ "obj-9", 0 ] 564 | } 565 | 566 | } 567 | , { 568 | "patchline" : { 569 | "destination" : [ "obj-15", 0 ], 570 | "disabled" : 0, 571 | "hidden" : 1, 572 | "source" : [ "obj-9", 2 ] 573 | } 574 | 575 | } 576 | , { 577 | "patchline" : { 578 | "destination" : [ "obj-17", 0 ], 579 | "disabled" : 0, 580 | "hidden" : 1, 581 | "source" : [ "obj-9", 3 ] 582 | } 583 | 584 | } 585 | , { 586 | "patchline" : { 587 | "destination" : [ "obj-4", 0 ], 588 | "disabled" : 0, 589 | "hidden" : 1, 590 | "source" : [ "obj-9", 1 ] 591 | } 592 | 593 | } 594 | ], 595 | "parameters" : { 596 | "obj-9::obj-13" : [ "live.text", "live.text", 0 ], 597 | "obj-9::obj-9" : [ "live.slider", "time", 0 ], 598 | "obj-9::obj-16" : [ "live.slider[1]", "velocity", 0 ], 599 | "obj-5" : [ "live.text[2]", "live.text[2]", 0 ], 600 | "obj-9::obj-14" : [ "live.text[1]", "live.text", 0 ] 601 | } 602 | , 603 | "dependency_cache" : [ { 604 | "name" : "humanize.js", 605 | "bootpath" : "/Users/adam/Music/Ableton/User Library/Presets/MIDI Effects/Max MIDI Effect/js-live-api-examples", 606 | "type" : "TEXT", 607 | "implicit" : 1 608 | } 609 | ], 610 | "latency" : 0, 611 | "project" : { 612 | "version" : 1, 613 | "creationdate" : 3485906455, 614 | "modificationdate" : 3485906455, 615 | "viewrect" : [ 0.0, 0.0, 300.0, 500.0 ], 616 | "autoorganize" : 1, 617 | "hideprojectwindow" : 1, 618 | "showdependencies" : 1, 619 | "autolocalize" : 0, 620 | "contents" : { 621 | "patchers" : { 622 | 623 | } 624 | , 625 | "code" : { 626 | 627 | } 628 | 629 | } 630 | , 631 | "layout" : { 632 | 633 | } 634 | , 635 | "searchpath" : { 636 | 637 | } 638 | , 639 | "detailsvisible" : 0 640 | } 641 | 642 | } 643 | 644 | } 645 | -------------------------------------------------------------------------------- /humanize.js: -------------------------------------------------------------------------------- 1 | function log() { 2 |   for(var i=0,len=arguments.length; i= 0) { 7 |         s = JSON.stringify(message); 8 |       } 9 |       post(s); 10 |     } 11 |     else if(message === null) { 12 |       post(""); 13 |     } 14 |     else { 15 |       post(message); 16 |     } 17 |   } 18 |   post("\n"); 19 | } 20 |   21 | // This debug logging is commented out when not actively developing 22 | // log("___________________________________________________"); 23 | // log("Reload:", new Date); 24 | 25 | 26 | //-------------------------------------------------------------------- 27 | // Clip class 28 |    29 | function Clip() { 30 |   var path = "live_set view highlighted_clip_slot clip"; 31 |   this.liveObject = new LiveAPI(path); 32 | } 33 |     34 | Clip.prototype.getLength = function() { 35 |   return this.liveObject.get('length'); 36 | } 37 |    38 | Clip.prototype._parseNoteData = function(data) { 39 |   var notes = []; 40 |   // data starts with "notes"/count and ends with "done" (which we ignore) 41 |   for(var i=2,len=data.length-1; i 127) return 127; 121 |   return this.pitch; 122 | } 123 |    124 | Note.prototype.getStart = function() { 125 |   // we convert to strings with decimals to work around a bug in Max 126 |   // otherwise we get an invalid syntax error when trying to set notes 127 |   if(this.start <= 0) return "0.0"; 128 |   return this.start.toFixed(4); 129 | } 130 |    131 | Note.prototype.getDuration = function() { 132 |   if(this.duration <= Note.MIN_DURATION) return Note.MIN_DURATION; 133 |   return this.duration.toFixed(4); // workaround similar bug as with getStart() 134 | } 135 |    136 | Note.prototype.getVelocity = function() { 137 |   if(this.velocity < 0) return 0; 138 |   if(this.velocity > 127) return 127; 139 |   return this.velocity; 140 | } 141 |    142 | Note.prototype.getMuted = function() { 143 |   if(this.muted) return 1; 144 |   return 0; 145 | } 146 |    147 | //-------------------------------------------------------------------- 148 | // Humanize behavior 149 |   150 | function humanize(type, maxTimeDelta, maxVelocityDelta) { 151 |   var humanizeVelocity = false, 152 |       humanizeTime = false; 153 |    154 |   switch(type) { 155 |     case "velocity": humanizeVelocity = true; break; 156 |     case "time": humanizeTime = true; break; 157 |     default: humanizeVelocity = humanizeTime = true; 158 |   } 159 |    160 |   if(!maxTimeDelta) maxTimeDelta = 0.05; 161 |   if(!maxVelocityDelta) maxVelocityDelta = 5; 162 |     163 |   clip = new Clip(); 164 |   notes = clip.getSelectedNotes(); 165 |   notes.forEach(function(note) { 166 |     if(humanizeTime) note.start += maxTimeDelta * (2*Math.random() - 1); 167 |     if(humanizeVelocity) note.velocity += maxVelocityDelta * (2*Math.random() - 1); 168 |   }); 169 |   clip.replaceSelectedNotes(notes); 170 | } 171 |       172 | //-------------------------------------------------------------------- 173 |   --------------------------------------------------------------------------------