{
   "ancestor_list" : [],
   "description" : "Roughly based on the stock ableton arpeggiator behavior. A lot of variation is possible combining custom ~chordSort and ~notePat functions.",
   "name" : "MIDI arpeggiator demo",
   "author" : "wondersluyter",
   "is_private" : null,
   "id" : "1-5eD",
   "code" : "(\r\n///////////////////////\r\n// input  parameters //\r\n/////////////////////////////////////////////////////////\r\n~hold = false;\r\n~clock = TempoClock(120/60);\r\n~rate = 1/24;\r\n~swing = 0.1;\r\n~jitter = 0.1;\r\n~legato = 2;\r\n//~chordSort = { |a, b| a.time < b.time }; // in order played\r\n~chordSort = { |a, b| a.num < b.num }; // lowest to highest\r\n//~chordSort = { |a, b| a.num > b.num }; // highest to lowest\r\n~notePat = { |chord| // play chord forwards and backwards\r\n  if (chord.size > 1) { \r\n    chord[0..chord.size-2] ++ chord.reverse[0..chord.size-2] \r\n  } { \r\n    chord \r\n  } \r\n};\r\n// ~notePat = { |chord| // interlace first note\r\n//   var temp;\r\n//   if (chord.size == 0) {\r\n//     chord\r\n//   } {\r\n//     temp = chord.removeAt(0);\r\n//     if (chord.size == 0) {\r\n//       chord\r\n//     } {\r\n//       [temp.dup(chord.size), chord].lace\r\n//     };\r\n//   }\r\n// };\r\n//~notePat = { |chord| chord }; // play chord forwards\r\n//~notePat = { |chord| chord.reverse }; // play chord backwards\r\n~transpose = 9.1;\r\n~steps = 1; // # of transpositions applied -- negative goes up and down\r\n~offset = 3; // offset into note pattern\r\n~repeats = inf;\r\n//~velocityFunc = { |vel| vel }; // velocity as played\r\n//~velocityFunc = { 64 }; // constant velocity\r\n~velocityFunc = { |vel, timeSincePlayed, beats| // something weirder\r\n  var ret = vel;\r\n  if (beats % 1 < 0.1) {\r\n    ret = (ret * 2)\r\n  } {\r\n    if (beats % 0.5 < 0.2) {\r\n      ret = (ret * 0.5)\r\n    } {\r\n      if (beats % 0.33333 < 0.1) {\r\n        ret = (ret * 1.7)\r\n      };\r\n    };\r\n  };\r\n  ret = ret * (1 / (1 + (timeSincePlayed * 0.1)));\r\n  ret;\r\n};\r\n/////////////////////////////////////////////////////////\r\n\r\n// MIDI\r\nMIDIClient.init; MIDIIn.connectAll;\r\n~notes = nil ! 127;\r\nMIDIdef.noteOn(\\keyOn, { |vel, num|\r\n  ~notes.do { |note, i|\r\n    if (note.notNil) {\r\n      if (note.pressed.not) {\r\n        ~notes[i] = nil;\r\n      };\r\n    };\r\n  };\r\n  if (~notes.select(_.notNil).select(_.pressed) == []) {\r\n    ~repeat_i = 0;\r\n  };\r\n  ~notes[num] = (num: num, vel: vel, time: thisThread.seconds, pressed: true);\r\n});\r\nMIDIdef.noteOff(\\keyOff, { |vel, num|\r\n  if (~hold) {\r\n    if (~notes[num].notNil) {\r\n      ~notes[num].pressed = false;\r\n    };\r\n  } {\r\n    ~notes[num] = nil;\r\n  };\r\n});\r\n\r\n// Pattern\r\n~repeat_i = 0;\r\n~ptr = 0;\r\n~i = 0;\r\nPdef(\\arp).stop;\r\nPdef(\\arp, Pbind(\r\n  \\instrument, \\default,\r\n  \\note_obj, Pfunc {\r\n    // construct chord with chordSort and notePat\r\n    var chord = ~notePat.(~notes.select(_.notNil).sort(~chordSort));\r\n    var ret;\r\n    var newChord;\r\n\r\n    // apply offset\r\n    if (~offset > 0) {\r\n      chord = chord.reverse;\r\n    };\r\n    ~offset.abs.do {\r\n      var temp = chord.pop;\r\n      chord = ([temp] ++ chord);\r\n    };\r\n    if (chord == [nil]) { chord = [] };\r\n    if (~offset > 0) {\r\n      chord = chord.reverse;\r\n    };\r\n\r\n    // apply transposition steps\r\n    newChord = chord;\r\n    ~steps.abs.do { |i|\r\n      newChord = newChord ++ chord.collect({ |note| note = note.copy; note.num = note.num + (~transpose * (i + 1)) });\r\n    };\r\n    if (~steps.sign.isNegative) {\r\n      (~steps.abs - 1).do { |i|\r\n        i = ~steps.abs - 2 - i;\r\n        newChord = newChord ++ chord.collect({ |note| note = note.copy; note.num = note.num + (~transpose * (i + 1)) });\r\n      }\r\n    };\r\n    chord = newChord;\r\n\r\n    // fetch appropriate note object from chord\r\n    if (chord.size > 0) {\r\n      if (~ptr >= chord.size) {\r\n        ~ptr = ~ptr % chord.size;\r\n        ~repeat_i = ~repeat_i + 1;\r\n      };\r\n      if (~repeat_i >= ~repeats) {\r\n        ret = (num: Rest(), vel: 0, time: 0);\r\n        ~ptr = 0;\r\n      } {\r\n        ret = chord[~ptr];\r\n        ~ptr = ~ptr + 1;\r\n      }\r\n    } {\r\n      ret = (num: Rest(), vel: 0, time: 0);\r\n      ~ptr = 0;\r\n    };\r\n    ret;\r\n  },\r\n  \\midinote, Pfunc { |event| event[\\note_obj].num },\r\n  \\db, Pfunc { |event|\r\n    var note = event[\\note_obj];\r\n    ~velocityFunc.(note.vel, thisThread.seconds - note.time, ~clock.beats).linlin(0, 127, -24, 0);\r\n  },\r\n  \\dur, Pfunc {\r\n    var base = ~rate * 4 * (1 + ~jitter.rand2);\r\n    var ret = if (~i == 0) { base * (1 + ~swing) } { base * (1 - ~swing) };\r\n    ~i = (~i + 1) % 2;\r\n    ret;\r\n  },\r\n  \\legato, Pfunc { ~legato }\r\n)).play(~clock);\r\n)\r\n\r\nPdef(\\arp).stop",
   "labels" : [
      "pattern",
      "arpeggio",
      "midi",
      "arpeggiator"
   ]
}
