Created
June 14, 2024 19:22
-
-
Save scztt/9e6b903d000ee705bf8ff035fee4bb6b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ( | |
| var guiFunc; | |
| var ampMin = -70; | |
| var ampMax = 36; | |
| var numberBoxHeight = 22; | |
| var oscDef; | |
| var compressFunc = { | |
| |isSynth, amp, aboveRatio, belowRatio, ratioScale, aboveThreshold, belowThreshold, knee, curve, expandMax, plot=false| | |
| var compressAmt, expandAmt; | |
| var kneeStart, kneeSlopeStart, kneeEnd, kneeSlopeEnd; | |
| var zero, curveMult, hermCurve; | |
| var compressCurve; | |
| zero = isSynth.if({ DC.ar(0) }, { 0 }); | |
| curveMult = isSynth.if( | |
| { | |
| aboveRatio > 1 | |
| }, | |
| { | |
| (aboveRatio > 1).if(1, 0) | |
| } | |
| ); | |
| aboveRatio = (1 / aboveRatio) * 2.pow(ratioScale.neg); | |
| belowRatio = (1 / belowRatio) * 2.pow(ratioScale.neg); | |
| knee = min(knee, (aboveThreshold - belowThreshold).abs / 2); | |
| compressAmt = (amp - aboveThreshold); | |
| compressAmt = compressAmt.linlin(knee.neg, knee, 0, knee) + (compressAmt - knee).clip(0, inf); | |
| aboveRatio = aboveRatio / (1 + (curve * compressAmt * curveMult)); | |
| compressAmt = compressAmt * (1 - aboveRatio); | |
| expandAmt = (belowThreshold - amp); | |
| expandAmt = expandAmt.linlin(knee.neg, knee, 0, knee) + (expandAmt - knee).clip(0, inf); | |
| expandAmt = expandAmt * (1 - belowRatio); | |
| expandAmt = expandAmt.clip(0, expandMax); | |
| [compressAmt, expandAmt]; | |
| }; | |
| // Ndef(\eqcomp).clear | |
| SynthDef.channelized(\compressor, | |
| channelizations: [1, 2, 4, 6, 8], | |
| ugenGraphFunc: { | |
| |numChannels=2| | |
| var in, bands, rms, peak, letters; | |
| var thresholdAdd, aboveRatioMul, belowRatioMul, postAmp; | |
| var attack, decay, solo, preGain, metadata, gain, knee, bypassAll, ratioScale, expandMax; | |
| var lag; | |
| var fLow, fMid, fHigh; | |
| var db = true; | |
| in = \in.ar(0 ! numChannels); | |
| // in = In.ar(\in.kr, 2); | |
| letters = ["a", "b", "c", "d"]; | |
| lag = \lagTime.kr(spec:ControlSpec(0, 10, warp:12, default:0.1)); | |
| preGain = \preGain.kr(spec:ControlSpec(ampMin, ampMax, \lin, default:0)).lag(lag); | |
| attack = \attack.kr(spec:ControlSpec(0, 0.2, \lin, default: 0.01)).lag(lag); | |
| decay = \decay.kr(spec:ControlSpec(0, 1, \lin, default: 0.1)).lag(lag); | |
| solo = \solo.kr(spec:ControlSpec(-1, 4, \lin, default: -1)).lag(lag); | |
| gain = \gain.kr(spec:ControlSpec(ampMin, ampMax, \lin, default: 0)).lag(lag); | |
| bypassAll = \bypasssAll.kr(spec:ControlSpec(0, 1, \lin, default: 0)).lag(lag); | |
| knee = \knee.kr(spec:ControlSpec(0, 12, default:3)).lag(lag); | |
| expandMax = \expandMax.kr(spec:ControlSpec(0, 36, default:24)).lag(lag); | |
| ratioScale = \ratioScale.kr(spec:ControlSpec(-4, 4, default:0)); | |
| fLow = \fLow.kr(spec:\freq.asSpec.copy.default_(100)); | |
| fMid = \fMid.kr(spec:\freq.asSpec.copy.default_(700)); | |
| fHigh = \fHigh.kr(spec:\freq.asSpec.copy.default_(4000)); | |
| rms = \rms.kr(spec:ControlSpec(0, 1, default:1)); | |
| rms = rms > 0; | |
| knee = max(knee, 0.00001); // avoid divide-by-zero | |
| metadata = Array.newClear((4*4)); | |
| in = in * preGain.dbamp; | |
| bands = BandSplitter4.ar(in, fLow, fMid, fHigh); | |
| bands = bands.collect { | |
| |band, i| | |
| var amp, ampDiff, preGain, belowThreshold, aboveThreshold, belowRatio, aboveRatio, bypass; | |
| var targetAmp, gain, compressAmt, expandAmt, curve; | |
| var mute, zero = DC.ar(0); | |
| preGain = "preGain_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(ampMin, ampMax, \lin, default: 0)); | |
| aboveThreshold = "aboveThreshold_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(ampMin, ampMax, \lin, default: -10)); | |
| belowThreshold = "belowThreshold_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(ampMin, ampMax, \lin, default: -40)); | |
| curve = "curve_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(0, 64, default:0)); | |
| aboveRatio = "aboveRatio_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(0.1, 200, \exp, default: 3)); | |
| belowRatio = "belowRatio_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(0.1, 200, \exp, default: 1)); | |
| gain = "gain_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(ampMin, ampMax, \lin, default: 0)); | |
| bypass = "bypass_%".format(letters[i]).asSymbol.kr(spec:ControlSpec(0, 1, \lin, default: 0)); | |
| bypass = max(bypassAll, bypass) > 0; | |
| #preGain, gain = K2A.ar([preGain, gain]).dbamp.lag(lag); | |
| mute = (solo >= 0) * ((solo - i).abs > 0.001); | |
| band = band * (mute < 1); | |
| band = band * blend(preGain, DC.ar(1), bypass); | |
| amp = blend( | |
| band.abs, | |
| RMS.ar(band), | |
| rms | |
| ); | |
| amp = ArrayMax.ar(amp)[0].ampdb.clip(-120, 24); | |
| #compressAmt, expandAmt = compressFunc.( | |
| true, | |
| amp, aboveRatio, belowRatio, | |
| ratioScale, aboveThreshold, belowThreshold, | |
| knee, curve, expandMax | |
| ); | |
| compressAmt = blend(compressAmt, zero, bypass); | |
| expandAmt = blend(expandAmt, zero, bypass); | |
| metadata[i*4] = amp; | |
| metadata[i*4+1] = (expandAmt - compressAmt).lagud(attack, decay); | |
| metadata[i*4+2] = (amp + (expandAmt - compressAmt).lagud(attack, decay)); | |
| metadata[i*4+3] = (amp + gain.ampdb + (expandAmt - compressAmt).lagud(attack, decay)); | |
| (mute <= 0) * ( | |
| band * gain * (expandAmt - compressAmt).lagud(attack, decay).dbamp | |
| ); | |
| }; | |
| bands = K2A.ar(gain).dbamp.lag(lag) * bands.sum; | |
| postAmp = blend( | |
| bands.abs, | |
| RMS.ar(bands), | |
| rms | |
| ); | |
| postAmp = ArrayMax.ar(postAmp)[0].ampdb.clip(-120, 24); | |
| metadata = metadata.add(postAmp); | |
| SendReply.ar(Impulse.ar(30), '/compressor', metadata, \replyId.kr(0)); | |
| bands.assertChannels(numChannels); | |
| Out.ar(\out.kr(0), bands); | |
| }); | |
| guiFunc = { | |
| |name, replyId| | |
| var plotMin = -50, plotMax = 6; | |
| var bands, makeVLabel, makeNumberView, makeBandView, makeBalanceView, view, makeSlider, makeMultiSlider, makeMeter; | |
| var bandView, thresholdAddSlider, aboveRatioSlider, below; | |
| var divider, bandMeters, balanceMeter, outputAmp=0, makeSolo, soloValue, bypassAll; | |
| var makePlot = { | |
| |data| | |
| var view, plotter; | |
| var changed; | |
| view = View().layout_(HLayout()); | |
| plotter = Plotter("plotter", Rect(0, 0, 100, 100), view); | |
| view.layout.add(plotter.interactionView); | |
| data.signal(\value).connectToUnique({ | |
| plotter.setValue( | |
| data, | |
| findSpecs: true, | |
| refresh: true, | |
| separately: true, | |
| minval: plotMin, | |
| maxval: plotMax | |
| ); | |
| }).freeAfter(view); | |
| view.fixedSize_(100@100); | |
| }; | |
| bands = ["a", "b", "c", "d"]; | |
| bandMeters = [0, 0, 0] ! 4; | |
| balanceMeter = [1, 1, 1, 1] / 4.0; | |
| makeVLabel = { | |
| |string| | |
| var size, font; | |
| string = string.asString; | |
| font = Font("Mplus 2", 16, false); | |
| size = string.bounds(font).insetBy(-3, -3).size; | |
| size = Size(size.height, size.width); | |
| UserView() | |
| .minSize_(size) | |
| .drawFunc_({ | |
| |v| | |
| Pen.fillColor = Color.grey(0.7); | |
| Pen.translate(0, v.bounds.height); | |
| Pen.rotate(-pi/2); | |
| Pen.stringInRect( | |
| string, | |
| Rect(0, 0, v.bounds.height, v.bounds.width) | |
| .insetBy(18, 6), | |
| font, | |
| alignment: QAlignment(\left) | |
| ); | |
| }) | |
| .canFocus_(false) | |
| .acceptsMouse_(false) | |
| .update() | |
| }; | |
| makeSolo = { | |
| |index, soloCV, bypassCV| | |
| var view, tb, soloAction, bypassAction; | |
| view = HLayout( | |
| tb = ToolBar(soloAction = MenuAction("solo").checkable_(true)), | |
| ToolBar(bypassAction = MenuAction("bypass").checkable_(true)), | |
| nil | |
| ); | |
| soloCV.signal(\value).connectTo({ | |
| |who, what, value| | |
| soloAction.checked = (value - index).abs < 0.001 | |
| }).freeAfter(tb); | |
| bypassCV.signal(\value).connectTo(bypassAction.methodSlot("checked_(value > 0)")).freeAfter(tb); | |
| bypassAction.signal(\toggled).connectTo(bypassCV.methodSlot("value_(value.if(1, 0))")).freeAfter(tb); | |
| soloAction.signal(\toggled).connectTo({ | |
| |who, what, value| | |
| if (value) { | |
| soloCV.value = index; | |
| } { | |
| soloCV.value = -1; | |
| } | |
| }).freeAfter(tb); | |
| view; | |
| }; | |
| makeNumberView = { | |
| |string, cv| | |
| var view; | |
| string = string.asString; | |
| view = NumberBox() | |
| .background_(Color.grey(1, 0.2)) | |
| .maxWidth_(60) | |
| .fixedHeight_(numberBoxHeight) | |
| .align_(\right) | |
| .font_(Font("Mplus 2", 14, false)); | |
| view.signal(\value).connectTo(cv.valueSlot).freeAfter(view); | |
| cv.signal(\value).connectTo(view.valueSlot).freeAfter(view); | |
| view; | |
| }; | |
| makeBalanceView = { | |
| var view = UserView() | |
| .animate_(true) | |
| .drawFunc_({ | |
| |v| | |
| var lastX = 0.0; | |
| var width = v.bounds.width; | |
| var height = v.bounds.height; | |
| Pen.fillColor = Color.hsv(0.1, 0.7, 1, 0.5); | |
| Pen.strokeColor = Color.hsv(0.1, 0.7, 1, 1); | |
| Pen.addRect(Rect( | |
| lastX, 0.0, | |
| floor(balanceMeter[0] * width), height | |
| )); | |
| Pen.fillStroke(); | |
| lastX = lastX + ceil(balanceMeter[0] * width); | |
| Pen.fillColor = Color.hsv(0.3, 0.7, 1, 0.5); | |
| Pen.strokeColor = Color.hsv(0.3, 0.7, 1, 1); | |
| Pen.addRect(Rect( | |
| lastX, 0.0, | |
| floor(balanceMeter[1] + width), height | |
| )); | |
| Pen.fillStroke(); | |
| lastX = lastX + ceil(balanceMeter[1] * width); | |
| Pen.fillColor = Color.hsv(0.5, 0.7, 1, 0.5); | |
| Pen.strokeColor = Color.hsv(0.5, 0.7, 1, 1); | |
| Pen.addRect(Rect( | |
| lastX, 0.0, | |
| floor(balanceMeter[2] * width), height | |
| )); | |
| Pen.fillStroke(); | |
| lastX = lastX + ceil(balanceMeter[2] * width); | |
| Pen.fillColor = Color.hsv(0.7, 0.7, 1, 0.5); | |
| Pen.strokeColor = Color.hsv(0.7, 0.7, 1, 1); | |
| Pen.addRect(Rect( | |
| lastX, 0.0, | |
| floor(balanceMeter[3] * width), height | |
| )); | |
| Pen.fillStroke(); | |
| }); | |
| view.fixedHeight_(16).minWidth_(100); | |
| }; | |
| makeBandView = { | |
| |name, values| | |
| var preGain, aboveThreshold, belowThreshold, aboveRatio, belowRatio, gain, amp, compression, expansion, bypass; | |
| var ampValue=0.5, compressionValue=0.2, expansionValue=0.1, postAmpValue=0; | |
| var plotData = FloatArray.newFrom(0!128), plotDataCalculate; | |
| var view = View().layout_( | |
| VLayout( | |
| HLayout( | |
| preGain = makeSlider.( | |
| "in gain", | |
| currentEnvironment["preGain_%".format(name).asSymbol] | |
| ), | |
| aboveThreshold = makeMultiSlider.( | |
| "threshold", | |
| currentEnvironment["aboveThreshold_%".format(name).asSymbol], | |
| currentEnvironment["belowThreshold_%".format(name).asSymbol] | |
| ), | |
| VLayout( | |
| UserView().fixedWidth_(40).drawFunc_({ | |
| |v| | |
| var bounds = v.bounds.moveTo(0, 0); | |
| var width; | |
| Pen.scale(1/2, 1); | |
| Pen.capStyle = 0; | |
| Pen.width = width = (bounds.width * 0.5) - 2; | |
| Pen.lineDash = FloatArray[2 / width, 1 / width]; | |
| Pen.strokeColor = Color.hsv(0.4, 0.5, 0.8, 0.85); | |
| Pen.line( | |
| (bounds.width*0.25)@bounds.height, | |
| (bounds.width*0.25)@(bounds.height * (1.0 - ampValue)), | |
| ); | |
| Pen.stroke(); | |
| Pen.width = width = (bounds.width * 1) - 2; | |
| Pen.lineDash = FloatArray[2 / width, 1 / width]; | |
| Pen.strokeColor = Color.hsv(0.4, 0.5, 0.8); | |
| Pen.line( | |
| (bounds.width*1.5)@bounds.height, | |
| (bounds.width*1.5)@(bounds.height * (1.0 - postAmpValue)), | |
| ); | |
| Pen.stroke(); | |
| // compress / expand rectangles | |
| Pen.fillColor = Color.hsv(0.55, 1, 0.8); | |
| Pen.fillRect(Rect( | |
| bounds.width * 0.5, | |
| bounds.height * (1.0 - ampValue), | |
| bounds.width * 0.5, | |
| bounds.height * (ampValue - postAmpValue).max(0) | |
| ).insetBy(1, 1)); | |
| Pen.fillColor = Color.hsv(0.75, 1, 0.8); | |
| Pen.fillRect(Rect( | |
| bounds.width * 0.5, | |
| bounds.height * (1.0 - postAmpValue), | |
| bounds.width * 0.5, | |
| bounds.height * (postAmpValue - ampValue).max(0) | |
| ).insetBy(1, 1)); | |
| }).animate_(true), | |
| StaticText() | |
| .string_(name) | |
| .align_(\center) | |
| .font_(Font("Mplus 2", 16)) | |
| .fixedHeight_(20), | |
| ), | |
| gain = makeSlider.( | |
| "out gain", | |
| currentEnvironment["gain_%".format(name).asSymbol] | |
| ), | |
| VLayout( | |
| nil, | |
| CVGridCell( | |
| "above", | |
| currentEnvironment["aboveRatio_%".format(name).asSymbol] | |
| ) | |
| .step_(0.1) | |
| .modStep_(0.02) | |
| .view, | |
| CVGridCell( | |
| "below", | |
| currentEnvironment["belowRatio_%".format(name).asSymbol] | |
| ) | |
| .step_(0.1) | |
| .modStep_(0.02) | |
| .view, | |
| CVGridCell( | |
| "curve", | |
| currentEnvironment["curve_%".format(name).asSymbol] | |
| ).view, | |
| makeSolo.( | |
| bands.indexOfEqual(name), | |
| ~solo, | |
| currentEnvironment["bypass_%".format(name).asSymbol], | |
| ), | |
| makePlot.(plotData), | |
| ) | |
| ) | |
| ).spacing_(0).margins_(0) | |
| ); | |
| values.connectTo({ | |
| ampValue = values[0].linlin(ampMin, ampMax, 0, 1); | |
| compressionValue = values[1].linlin(0, ampMin.neg, 0, 1); | |
| expansionValue = values[1].neg.linlin(0, ampMax, 0, 1); | |
| postAmpValue = values[2].linlin(ampMin, ampMax, 0, 1); | |
| }).freeAfter(view); | |
| plotDataCalculate = { | |
| |i| | |
| var amps = FloatArray.newFrom([plotMin, plotMax].resamp1(128)); | |
| var compress = compressFunc.( | |
| false, | |
| amps, | |
| currentEnvironment["aboveRatio_%".format(name).asSymbol].value, | |
| currentEnvironment["belowRatio_%".format(name).asSymbol].value, | |
| ~ratioScale.value, | |
| currentEnvironment["aboveThreshold_%".format(name).asSymbol].value, | |
| currentEnvironment["belowThreshold_%".format(name).asSymbol].value, | |
| ~knee.value, | |
| currentEnvironment["curve_%".format(name).asSymbol].value, | |
| ~expandMax.value | |
| ); | |
| compress = amps - compress[0] + compress[1]; | |
| compress.do { |v, i| plotData[i] = v }; | |
| plotData.changed(\value); | |
| }.e(currentEnvironment); | |
| [ | |
| currentEnvironment["aboveRatio_%".format(name).asSymbol], | |
| currentEnvironment["belowRatio_%".format(name).asSymbol], | |
| ~ratioScale, | |
| currentEnvironment["aboveThreshold_%".format(name).asSymbol], | |
| currentEnvironment["belowThreshold_%".format(name).asSymbol], | |
| ~knee, | |
| currentEnvironment["curve_%".format(name).asSymbol], | |
| ~expandMax | |
| ].do { | |
| |cv| | |
| cv.signal(\value).connectTo(plotDataCalculate).collapse(0.1).freeAfter(view); | |
| }; | |
| view; | |
| }; | |
| makeMultiSlider = { | |
| |name, cvHi, cvLo| | |
| var slider, view; | |
| slider = RangeSlider() | |
| .maxWidth_(60) | |
| .canFocus_(false); | |
| cvLo.signal(\input).connectTo(slider.valueSlot(\lo)); | |
| cvHi.signal(\input).connectTo(slider.valueSlot(\hi)); | |
| slider.signal(\lo).connectTo(cvLo.inputSlot); | |
| slider.signal(\hi).connectTo(cvHi.inputSlot); | |
| View().layout_(VLayout( | |
| makeNumberView.(name, cvHi), | |
| StackLayout( | |
| makeVLabel.(name), | |
| slider, | |
| ).mode_(\stackAll), | |
| makeNumberView.(name, cvLo), | |
| ).spacing_(0).margins_(0)) | |
| }; | |
| makeSlider = { | |
| |name, cv| | |
| var slider, view; | |
| slider = Slider() | |
| .maxWidth_(60) | |
| .canFocus_(false); | |
| cv.signal(\input).connectTo(slider.valueSlot); | |
| slider.signal(\value).connectTo(cv.inputSlot); | |
| View().layout_(VLayout( | |
| numberBoxHeight, | |
| StackLayout( | |
| makeVLabel.(name), | |
| slider, | |
| ).mode_(\stackAll).spacing_(0).margins_(0), | |
| HLayout( | |
| nil, | |
| makeNumberView.(name, cv) | |
| ) | |
| ).spacing_(0).margins_(0)) | |
| }; | |
| makeMeter = {}; | |
| divider = { | |
| View().fixedWidth_(2).background_(Color.grey); | |
| }; | |
| view = View(bounds:400@200).layout_(HLayout( | |
| makeSlider.("pre gain", ~preGain), divider.(), | |
| makeBandView.("a", bandMeters[0]), divider.(), | |
| makeBandView.("b", bandMeters[1]), divider.(), | |
| makeBandView.("c", bandMeters[2]), divider.(), | |
| makeBandView.("d", bandMeters[3]), divider.(), | |
| VLayout( | |
| HLayout( | |
| UserView().fixedWidth_(40).animate_(true).drawFunc_({ | |
| |v| | |
| var bounds = v.bounds.moveTo(0, 0); | |
| Pen.scale(1/3, 1); | |
| Pen.fillColor = Color.hsv(0.4, 0.5, 0.8); | |
| Pen.fillRect(Rect( | |
| 0, | |
| bounds.height * (1.0 - outputAmp), | |
| bounds.width, | |
| bounds.height * outputAmp | |
| ).insetBy(1, 1)); | |
| }), | |
| makeSlider.("post gain", ~gain), | |
| ), | |
| CVGridCell( | |
| "knee", | |
| ~knee, | |
| ).view, | |
| CVGridCell( | |
| "ratio *", | |
| ~ratioScale, | |
| ).view, | |
| CVGridCell( | |
| "attack", | |
| ~attack, | |
| ).view, | |
| CVGridCell( | |
| "decay", | |
| ~decay, | |
| ).view, | |
| makeBalanceView.(), | |
| ToolBar( | |
| bypassAll = MenuAction("bypass all").checkable_(true), | |
| ), | |
| ), | |
| nil | |
| )); | |
| bypassAll.signal(\value).connectTo(bypassAll.methodSlot("checked_(value > 0)")).freeAfter(view); | |
| bypassAll.signal(\toggled).connectTo(bypassAll.methodSlot("checked_(value.if(1, 0))")).freeAfter(view); | |
| oscDef = OSCdef("compressor_%".format(name).asSymbol, Routine({ | |
| |msg| | |
| var newBalanceMeter, newPeak, lastPeak = 0, lastPeakTime = 0; | |
| newBalanceMeter = balanceMeter.copy; | |
| inf.do { | |
| msg = msg[3..]; | |
| outputAmp = msg.pop.linlin(ampMin, ampMax, 0, 1); | |
| msg.clump(4).do { | |
| |v, i| | |
| bandMeters[i][0] = v[0]; | |
| bandMeters[i][1] = v[1]; | |
| bandMeters[i][2] = v[2]; | |
| newBalanceMeter[i] = v[3].dbamp.max(-60.dbamp); | |
| }; | |
| newPeak = newBalanceMeter.sum; | |
| if (newPeak > lastPeak or: { (thisThread.seconds - lastPeakTime) > 1 }) { | |
| newBalanceMeter.do({ |v, i| balanceMeter[i] = v }); | |
| lastPeak = newPeak; | |
| lastPeakTime = thisThread.seconds; | |
| balanceMeter = balanceMeter.normalizeSum(); | |
| }; | |
| bandMeters.do(_.changed); | |
| msg = 0.yield; | |
| } | |
| }), "/compressor").permanent_(true); | |
| view.onClose = view.onClose.addFunc({ | |
| oscDef.clear; | |
| }); | |
| view.name = name; | |
| view.front; | |
| }; | |
| e = currentEnvironment; | |
| Pdef(\compressor, { | |
| var path = thisProcess.nowExecutingPath; | |
| var name = (~name !? ("%_eqcomp".format(_)) ?? { "eqcomp" }); | |
| var replyId = UniqueID.next; | |
| var channels = ~channels ?? { 2 }; | |
| "CREATING COMPRESSOR: %".format(replyId).postln; | |
| " CHANNELS: %".format(channels).postln; | |
| if (~c.envir.size == 0) { | |
| "INITIALIZING COMPRESSOR".postln; | |
| ~c.guiFunc = { | |
| guiFunc.(name, replyId).autoRememberPosition(name.asSymbol); | |
| }; | |
| ~c.setSpecs(\compressor.asSynthDesc.specs); | |
| }; | |
| ~c.storeCurrent(name); | |
| Pbind.mono( | |
| \instrument, "compressor_%ch".format(channels).asSymbol, | |
| \dur, 16, | |
| \replyId, replyId, | |
| *(~c.asSynthMapArgs) | |
| ) | |
| }); | |
| ) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment