{
   "description" : "I needed an Env editor with knobs to edit individual points. Maybe it can be useful to someone. You can zoom horizontally with shift+mouseDrag. Show also a way to write classes with events",
   "ancestor_list" : [],
   "author" : "grirgz",
   "name" : "Env editor with knobs to edit individual points",
   "is_private" : null,
   "id" : "1-4V3",
   "code" : "(\r\n\t~windowize = { arg layout;\r\n\t\tvar win;\r\n\t\twin = Window.new;\r\n\t\twin.layout = layout;\r\n\t\twin.front;\r\n\t};\r\n\t~env_controller = (\r\n\t\tnew: { arg self, env, spec;\r\n\t\t\tself = self.deepCopy;\r\n\t\t\r\n\t\t\t\r\n\t\t\tself.get_env = env;\r\n\t\t\tself.spec = spec ?? [3, \\freq.asSpec];\r\n\r\n\t\t\tspec = self.spec.value[1];\r\n\t\t\tspec = spec ?? self.label.asSpec ?? \\widefreq.asSpec;\r\n\r\n\t\t\tself.level_spec = spec;\r\n\t\t\tself.time_spec = ControlSpec(0.01,8,\\exp,0,0.1);\r\n\t\t\tself.curve_spec = ControlSpec(-9,9,\\lin,0,0);\r\n\t\t\r\n\t\t\tself;\r\n\t\t},\r\n\r\n\t\tget_xy: { arg self;\r\n\t\t\tvar x = List.new;\r\n\t\t\tvar y = List.new;\r\n\t\t\tvar env = self.get_env;\r\n\t\t\tenv.levels.do { arg lvl, n;\r\n\t\t\t\ty.add(lvl);\r\n\t\t\t\tif(n == 0) {\r\n\t\t\t\t\tx.add(0);\r\n\t\t\t\t} {\r\n\t\t\t\t\tx.add(env.times[n-1])\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t\t[x.integrate.asArray,y.asArray]\r\n\t\t},\r\n\t\ttime_scale: 1/2,\r\n\r\n\t\tget_norm_xy: { arg self, time_scale;\r\n\t\t\tvar x = List.new;\r\n\t\t\tvar y = List.new;\r\n\t\t\tvar env = self.get_env;\r\n\t\t\tvar times, levels;\r\n\t\t\tvar res;\r\n\t\t\ttime_scale = time_scale ?? self.time_scale;\r\n\t\t\ttimes = self.time_spec.unmap(env.times);\r\n\t\t\tlevels = self.level_spec.unmap(env.levels);\r\n\t\t\tx = times;\r\n\t\t\ty = levels;\r\n\t\t\tx = x.insert(0, 0);\r\n\t\t\tres = [x.integrate.asArray * time_scale,y.asArray];\r\n\t\t\tres\r\n\t\t},\r\n\r\n\t\tset_norm_xy: { arg self, val, time_scale;\r\n\t\t\tvar times, levels;\r\n\t\t\ttime_scale = time_scale ?? self.time_scale;\r\n\t\t\t#times, levels = val;\r\n\t\t\ttimes = times.copy;\r\n\t\t\ttimes.removeAt(0);\r\n\t\t\ttimes = times / time_scale;\r\n\t\t\ttimes = times.differentiate;\r\n\t\t\ttimes = self.time_spec.map(times);\r\n\t\t\tlevels = self.level_spec.map(levels);\r\n\t\t\tself.get_env.times = times;\r\n\t\t\tself.get_env.levels = levels;\r\n\t\t},\r\n\r\n\t\tedit: { arg self;\r\n\t\t\t~env_editor.new(self).make_window;\r\n\t\t},\r\n\r\n\t\tlabel: \\bla\r\n\r\n\t);\r\n\r\n\t~env_to_string = { arg env;\r\n\t\tvar prec = 5;\r\n\t\t\"Env(%, %, %)\".format(\r\n\t\t\tenv.levels.collect({ arg x; x.asFloat.asStringPrec(prec)}),\r\n\t\t\tenv.times.collect({arg x; x.asFloat.asStringPrec(prec)}),\r\n\t\t\tenv.curves\r\n\t\t);\r\n\t};\r\n\t\r\n\t~env_editor = (\r\n\t\tnew: { arg self, controller;\r\n\t\t\tself = self.deepCopy;\r\n\t\t\r\n\t\t\tself.controller = controller;\r\n\t\t\r\n\t\t\tself;\r\n\t\t},\r\n\r\n\r\n\t\tupdate_label: { arg self;\r\n\t\t\tself.env_label.string_(\r\n\t\t\t\t\"%\".format(\r\n\t\t\t\t\t~env_to_string.(self.controller.get_env),\r\n\t\t\t\t)\r\n\t\t\t);\r\n\t\t},\r\n\t\t\r\n\t\tupdate_env: { arg self;\r\n\t\t\tself.env_view.value_(self.controller.get_norm_xy);\r\n\t\t},\r\n\r\n\t\tupdate_knobs: { arg self;\r\n\t\t\tvar index = self.env_view.index;\r\n\t\t\tvar levels = self.controller.get_env.levels;\r\n\t\t\tvar times = self.controller.get_env.times;\r\n\t\t\tvar level_spec = self.controller.level_spec;\r\n\t\t\tvar time_spec = self.controller.time_spec;\r\n\t\t\tif(levels[index].notNil) {\r\n\t\t\t\tself.knob_level.value = level_spec.unmap(levels[index]);\r\n\t\t\t};\r\n\t\t\tif(times[index].notNil) {\r\n\t\t\t\tself.knob_time.value = time_spec.unmap(times[index]);\r\n\t\t\t};\r\n\t\t},\r\n\r\n\t\tmake_layout: { arg self;\r\n\t\t\tvar node_name, name, spec;\r\n\t\t\tvar win, val, slider, label, layout;\r\n\t\t\tvar prec = 0.001;\r\n\t\t\tvar knob_layout;\r\n\t\t\tvar knob_size = 15;\r\n\t\t\tvar level_spec, time_spec, curve_spec;\r\n\t\t\tname = self.controller.label;\r\n\r\n\t\t\tlevel_spec = self.controller.level_spec;\r\n\t\t\ttime_spec = self.controller.time_spec;\r\n\t\t\tcurve_spec = self.controller.curve_spec;\r\n\r\n\t\t\tlabel = StaticText.new;\r\n\t\t\tlabel.string = name.asString;\r\n\t\t\tlabel.minWidth = 160;\r\n\r\n\t\t\tval = StaticText.new;\r\n\t\t\tval.minWidth = 100;\r\n\t\t\tval.string = self.controller.get_val;\r\n\t\t\t\r\n\r\n\t\t\tslider = EnvelopeView.new(nil, Rect(0, 0, 230, 80))\r\n\t\t\t\t.drawLines_(true)\r\n\t\t\t\t.selectionColor_(Color.red)\r\n\t\t\t\t.drawRects_(true)\r\n\t\t\t\t.step_(0)\r\n\t\t\t\t.thumbSize_(10)\r\n\t\t\t\t.keepHorizontalOrder_(true)\r\n\t\t\t\t.value_(self.controller.get_norm_xy);\r\n\t\t\tself.env_view = slider;\r\n\t\t\tself.env_view.selectIndex(1);\r\n\t\t\tself.env_view.action = { arg env;\r\n\t\t\t\tif(env.index == 0 and: { env.x > 0 }) {\r\n\t\t\t\t\tenv.x = 0;\r\n\t\t\t\t};\r\n\t\t\t\tself.controller.set_norm_xy(env.value);\r\n\t\t\t\tself.update_label;\r\n\t\t\t\tself.update_knobs;\r\n\t\t\t};\r\n\t\t\tself.env_view.mouseDownAction = { arg view, x, y, mod;\r\n\t\t\t\tself.mouse_down_point = Point(x,y);\r\n\t\t\t\tself.mouse_down_index = view.index;\r\n\t\t\t\tself.mouse_down_time_scale = self.controller.time_scale;\r\n\t\t\t};\r\n\t\t\tself.env_view.mouseUpAction = { arg view, x, y, mod;\r\n\t\t\t\tself.update_knobs;\r\n\t\t\t\tif(mod.isShift) {\r\n\t\t\t\t\tself.env_view.selectIndex(self.mouse_down_index)\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t\tself.env_view.mouseMoveAction = { arg view, x, y, mod;\r\n\t\t\t\tvar val;\r\n\t\t\t\tif(mod.isShift) {\r\n\t\t\t\t\tval = x - self.mouse_down_point.x;\r\n\t\t\t\t\tself.controller.time_scale = self.mouse_down_time_scale + (val/500);\r\n\t\t\t\t\tself.update_env;\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t\tself.env_view.keyDownAction = {\r\n\t\t\t\tself.update_knobs;\r\n\t\t\t};\r\n\r\n\t\t\tself.knob_level = Knob.new\r\n\t\t\t\t.mode_(\\vert)\r\n\t\t\t\t.maxWidth_(knob_size);\r\n\t\t\tself.knob_time = Knob.new\r\n\t\t\t\t.mode_(\\vert)\r\n\t\t\t\t.maxWidth_(knob_size);\r\n\t\t\tself.knob_curve = Knob.new\r\n\t\t\t\t.mode_(\\vert)\r\n\t\t\t\t.maxWidth_(knob_size);\r\n\r\n\t\t\tself.knob_level.action = { arg knob;\r\n\t\t\t\tvar index = self.env_view.index;\r\n\t\t\t\tvar levels = self.controller.get_env.levels;\r\n\t\t\t\tif(levels[index].notNil) {\r\n\t\t\t\t\tlevels[index] = level_spec.map(knob.value);\r\n\t\t\t\t\tself.controller.get_env.levels = levels;\r\n\t\t\t\t\tself.env_view.y = knob.value;\r\n\t\t\t\t\tself.update_label;\r\n\t\t\t\t};\r\n\t\t\t};\r\n\t\t\tself.knob_time.action = { arg knob;\r\n\t\t\t\tvar index = self.env_view.index;\r\n\t\t\t\tvar times = self.controller.get_env.times;\r\n\t\t\t\tif(times[index].notNil) {\r\n\t\t\t\t\ttimes[index] = time_spec.map(knob.value);\r\n\t\t\t\t\tself.controller.get_env.times = times;\r\n\t\t\t\t\tself.env_view.value_(self.controller.get_norm_xy);\r\n\t\t\t\t\tself.env_view.selectIndex(index);\r\n\t\t\t\t\tself.update_label;\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t\tself.knob_curve.action = { arg knob;\r\n\t\t\t\tself.controller.get_env.curves = curve_spec.map(knob.value);\r\n\t\t\t\tself.env_view.curves = curve_spec.map(knob.value);\r\n\t\t\t\tself.update_label;\r\n\t\t\t};\r\n\r\n\t\t\tself.env_label = StaticText.new\r\n\t\t\t\t.font_(Font(\"Arial\",10))\r\n\t\t\t\t.string_(\"--\");\r\n\r\n\t\t\tknob_layout = HLayout.new(\r\n\t\t\t\tself.knob_level,\r\n\t\t\t\tself.knob_time,\r\n\t\t\t\tself.knob_curve,\r\n\t\t\t\tself.env_label\r\n\t\t\t);\r\n\r\n\r\n\t\t\tself.update_label;\r\n\t\t\tself.update_knobs;\r\n\r\n\t\t\tlayout = VLayout.new(\r\n\t\t\t\tlabel, slider, knob_layout\r\n\t\t\t);\r\n\t\t\tself.layout = layout;\r\n\t\t\tlayout;\r\n\t\t\t\r\n\t\t},\r\n\r\n\t\tmake_window: { arg self;\r\n\t\t\t~windowize.(self.make_layout);\r\n\t\t},\r\n\r\n\t);\r\n\t~ctrl = ~env_controller.new(Env([80,1500.1,90.51,300,400],[0.1,0.2,0.1,0.1]), [0, \\freq.asSpec]);\r\n)\r\n\r\n(\r\nSynthDef(\\ftest, { arg out=0, amp=0.1, gate=1, pan=0, spread=0.8, freq=200, doneAction=2;\r\n\tvar sig, sig1, sig2, sig3;\r\n\tfreq = freq + EnvGen.kr(\\fenv.kr(Env([80,500.1,90.51,300,400],[0.1,0.2,0.1,0.1])), gate);\r\n\tsig = SinOsc.ar(freq);\r\n\tsig = sig * EnvGen.ar(\\adsr.kr(Env.adsr(0.01,0.1,0.8,0.1)),gate,doneAction:doneAction);\r\n\tsig = Splay.ar(sig, spread, amp, pan);\r\n\tOut.ar(out, sig);\r\n}).add;\r\n);\r\n\r\n(\r\n\r\nPdef(\\plop, Pbind(\r\n\t\\instrument, \\ftest,\r\n\t\\fenv, Pfunc ({ ~ctrl.get_env  }),\r\n\t\\freq, 200,\r\n\t\\dur, 1,\r\n\t\\amp, 0.1\r\n)).play;\r\n)\r\n\r\n~ctrl.edit;",
   "labels" : [
      "gui",
      "tool",
      "editor"
   ]
}
