{
   "is_private" : null,
   "id" : "1-5hC",
   "code" : "(\r\ns.options.numWireBufs = 128; // 256;\r\n\r\n~numSliders = 64; // Tested at up to 120\r\n~mul = 0.1;\r\n~freq = 200; // 393.44\r\n\r\ns.waitForBoot {\r\n    var numSliders, indexThumbSize, indexWidth, centre, upperHeight, lowerHeight, multiSliderHeight, width, win;\r\n    var freqBox, multiSliderFreq, multiSliderPhase, phases, phaseButtons, ampNum, ampSlider, freqScope;\r\n    var lable, lables, waveformView, waveform, interactivePhaseButtonsUpdate;\r\n    var lablesHeight, interactiveLableFreq, interactiveLablePhase, interactiveLableUpdate, antiAliasing;\r\n\r\n    numSliders = ~numSliders;\r\n    indexThumbSize = 16;\r\n    indexWidth = indexThumbSize + 1;\r\n    centre = [Window.screenBounds.width, Window.screenBounds.height] / 2;\r\n\r\n    width = if(numSliders <= 36) {\r\n        36 * indexWidth + 6 + 255;\r\n    } {\r\n        numSliders * indexWidth + 6\r\n    };\r\n\r\n    win = Window(\"overtone explorer\", Rect(centre[0] - (width / 2), centre[1] - 325, width, 650));\r\n    win.front.onClose_{ x.free; freqScope.kill }.acceptsMouseOver_(true);\r\n\r\n    lowerHeight = 250;\r\n    upperHeight = win.bounds.height - lowerHeight;\r\n    multiSliderHeight = upperHeight - 50 / 2;\r\n\r\n    lable = { |parent, left, top, width, height, align, fontSize, string, colour, background|\r\n        StaticText(parent, Rect(left, top, width, height))\r\n        .align_(align)\r\n        .font_(Font(\"Arial\", fontSize))\r\n        .stringColor_(colour)\r\n        .string_(string)\r\n        .background_(background)\r\n    };\r\n\r\n    lablesHeight = 36;\r\n\r\n    antiAliasing = { |freqBase|\r\n        numSliders.do { |index|\r\n            var thisFreq;\r\n            thisFreq = index + 1 * freqBase;\r\n            interactiveLableFreq.string_(\"\");\r\n            if(thisFreq < (s.sampleRate / 2)) {\r\n                lables[index].size.do { |i|\r\n                    lables[index][i].stringColor_(Color.black)\r\n                }\r\n            } {\r\n                multiSliderFreq.value_(multiSliderFreq.value.put(index, 0));\r\n                lables[index].size.do { |i|\r\n                    lables[index][i].stringColor_(Color.grey(0.6))\r\n                }\r\n            }\r\n        }\r\n    };\r\n\r\n    multiSliderFreq = if(numSliders < 36) {\r\n        MultiSliderView(win, Rect(1, 0, numSliders * indexWidth + 6, multiSliderHeight));\r\n    } {\r\n        MultiSliderView(win, Rect(1, 0, width, multiSliderHeight));\r\n    };\r\n\r\n    multiSliderFreq.value_((0 ! numSliders))\r\n    .showIndex_(true)\r\n    .indexThumbSize_(indexThumbSize)\r\n    .action_{ |sliders|\r\n        var numNotZero = ~numSliders - sliders.value.occurrencesOf(0);\r\n        x.set(\\numNotZero, numNotZero, \\overtoneLast, ~numSliders, \\amplitudes, sliders.value)\r\n    }\r\n    .mouseOverAction_{ |view, x, y|\r\n        var mouseOverIndex;\r\n        mouseOverIndex = (x / indexWidth).round;\r\n        interactiveLableUpdate.(x, y, multiSliderFreq, interactiveLableFreq, \"Hz;\\n\", 100, \"%\",\r\n            \"\\n0 % to prevent aliasing\");\r\n        interactiveLableFreq.stringColor_(if(mouseOverIndex * freqBox.value < (s.sampleRate / 2)) {\r\n            Color.blue(1, 0.5)\r\n        } {\r\n            Color.grey(0.5)\r\n        });\r\n    }\r\n    .mouseMoveAction_{ |view, x, y|\r\n        var mouseMoveIndex;\r\n        mouseMoveIndex = (x / indexWidth).round;\r\n        interactiveLableUpdate.(x, y, multiSliderFreq, interactiveLableFreq, \"Hz;\\n\", 100, \"%\",\r\n            \"\\n0 % to prevent aliasing\");\r\n        interactiveLableFreq.stringColor_(if(mouseMoveIndex * freqBox.value < (s.sampleRate / 2)) {\r\n            Color.blue(1, 0.5)\r\n        } {\r\n            Color.grey(0.5)\r\n        });\r\n    }\r\n    .mouseUpAction_{ |view, x, y|\r\n        antiAliasing.(freqBox.value);\r\n        interactiveLableUpdate.(x, y, multiSliderFreq, interactiveLableFreq, \"Hz;\\n\", 100, \"%\",\r\n            \"\\n0 % to prevent aliasing\")\r\n    }\r\n    .mouseLeaveAction_{\r\n        interactiveLableFreq.string_(\"Amplitudes\")\r\n        .bounds_(Rect(\r\n            multiSliderFreq.bounds.extent.x - 120 / 2,\r\n            multiSliderFreq.bounds.extent.y - 20 / 2,\r\n            120,\r\n            25));\r\n        multiSliderFreq.showIndex_(false)\r\n    }\r\n    .mouseEnterAction_{\r\n        multiSliderFreq.showIndex_(true)\r\n    }\r\n    .background_(Color.grey(0.9, 0.1))\r\n    .colors_(Color.grey(0.3, 0.5), Color.grey(0.3, 0.5));\r\n\r\n    interactiveLableFreq = lable.(multiSliderFreq,\r\n        multiSliderFreq.bounds.extent.x - 120 / 2,\r\n        multiSliderFreq.bounds.extent.y - 20 / 2,\r\n        120,\r\n        25,\r\n        \\left, 24, \"Amplitudes\", Color.blue(1, 0.5), Color.grey(0.9, 0.7));\r\n\r\n    lables = numSliders.collect { |i|\r\n        var string, left;\r\n        string = (i + 1).asString.padLeft(3, \"0\");\r\n        left = i * indexWidth + 7;\r\n        [\r\n            lable.(win, left, multiSliderHeight + 2,  10, 10, \\center, 11, string[0], Color.black, Color.clear),\r\n            lable.(win, left, multiSliderHeight + 14, 10, 10, \\center, 11, string[1], Color.black, Color.clear),\r\n            lable.(win, left, multiSliderHeight + 26, 10, 10, \\center, 11, string[2], Color.black, Color.clear)\r\n        ]\r\n    };\r\n\r\n    phases = 0 ! ~numSliders;\r\n\r\n    phaseButtons = ~numSliders.collect { |i|\r\n        Button(win, Rect(\r\n            indexThumbSize + 1 * i + 4, upperHeight / 2 - 15 + 26, indexThumbSize, indexThumbSize\r\n        ))\r\n        .states_([[\"O\", Color.black], [\"Ø\", Color.green(0.6)]])\r\n        .action = { |view|\r\n            var phase = if(view.value == 0) { 0 } { pi };\r\n            if(phaseButtons[i].states.size == 1) {\r\n                phaseButtons[i]\r\n                .states_([[\"O\", Color.black], [\"Ø\", Color.green(0.6)]])\r\n            };\r\n            multiSliderPhase.value = multiSliderPhase.value.put(i, phase.linlin(0, 2pi, 0, 1));\r\n            phases.put(i, phase);\r\n            x.set(\\phases, phases)\r\n        }\r\n    };\r\n\r\n    multiSliderPhase = if(numSliders < 36) {\r\n        MultiSliderView(win, Rect(\r\n            1, multiSliderHeight + lablesHeight + indexThumbSize,\r\n            numSliders * indexWidth + 6, multiSliderHeight\r\n        ));\r\n    } {\r\n        MultiSliderView(win, Rect(\r\n            1, multiSliderHeight + lablesHeight + indexThumbSize, width, multiSliderHeight\r\n        ));\r\n    };\r\n\r\n    multiSliderPhase.value_(((0 ! numSliders)))\r\n    .showIndex_(true)\r\n    .indexThumbSize_(indexThumbSize)\r\n    .action_{ |sliders|\r\n        var phases = sliders.value.linlin(0, 1, 0, 2pi);\r\n        interactivePhaseButtonsUpdate.(sliders.index);\r\n        x.set(\\phases, phases)\r\n    }\r\n    .mouseOverAction_{ |view, x, y|\r\n        var mouseOverIndex;\r\n        mouseOverIndex = (x / indexWidth).round;\r\n        interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, \"Hz;\\n\", 360, \"°\", \"\");\r\n        interactiveLablePhase.stringColor_(if(mouseOverIndex * freqBox.value < (s.sampleRate / 2)) {\r\n            Color.blue(1, 0.5)\r\n        } {\r\n            Color.grey(0.5)\r\n    })    }\r\n    .mouseMoveAction_{ |view, x, y|\r\n        var mouseMoveIndex;\r\n        mouseMoveIndex = (x / indexWidth).round.clip(0, ~numSliders - 1);\r\n        interactivePhaseButtonsUpdate.(mouseMoveIndex);\r\n        interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, \"Hz;\\n\", 360, \"°\", \"\");\r\n        interactiveLablePhase.stringColor_(if(mouseMoveIndex * freqBox.value < (s.sampleRate / 2)) {\r\n            Color.blue(1, 0.5)\r\n        } {\r\n            Color.grey(0.5)\r\n        })    }\r\n    .mouseUpAction_{ |view, x, y|\r\n        interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, \"Hz;\\n\", 360, \"°\", \"\")\r\n    }\r\n    .mouseLeaveAction_{\r\n        interactiveLablePhase.string_(\"Phases\")\r\n        .bounds_(Rect(\r\n            multiSliderPhase.bounds.extent.x - 100 / 2,\r\n            multiSliderPhase.bounds.extent.y - 20 / 2,\r\n            100,\r\n            25));\r\n        multiSliderPhase.showIndex_(false)\r\n    }\r\n    .mouseEnterAction_{\r\n        multiSliderPhase.showIndex_(true)\r\n    }\r\n    .background_(Color.grey(0.9, 0.1))\r\n    .colors_(Color.grey(0.3, 0.5), Color.grey(0.3, 0.5));\r\n\r\n    interactiveLablePhase = lable.(multiSliderPhase,\r\n        multiSliderPhase.bounds.extent.x - 100 / 2,\r\n        multiSliderPhase.bounds.extent.y - 20 / 2,\r\n        100,\r\n        25,\r\n        \\left, 24, \"Phases\", Color.blue(1, 0.5), Color.grey(0.9, 0.7));\r\n\r\n    interactiveLableUpdate = { |x, y, where, which, hzORphase, how, unit, antialiasing|\r\n        var index, indexLable, indexLableSize, value, cps;\r\n        index =(x / indexWidth).ceil.clip(1, ~numSliders);\r\n        indexLable = index.asInteger.asString;\r\n        indexLableSize = indexLable.size;\r\n        value = y.linlin(0, where.bounds.extent.y - 1, how, 0).asInteger;\r\n        cps = index * freqBox.value;\r\n        which.string_(\r\n            indexLable ++ \":\" + cps ++ hzORphase + (\" \" ! (indexLableSize + 2)).join + value ++ unit +\r\n            (if(s.sampleRate / 2 <= cps) { antialiasing } { \"\" })\r\n        );\r\n\r\n        which.bounds = which.bounds.size_(which.sizeHint);\r\n\r\n        which.bounds_(\r\n            x = if(x + which.bounds.width < where.bounds.extent.x) {\r\n                x\r\n            } {\r\n                x - which.bounds.width\r\n            };\r\n            y = where.bounds.extent.y - which.bounds.height / 2;\r\n            Rect(x, y, which.bounds.width, which.bounds.height);\r\n        )\r\n    };\r\n\r\n    interactivePhaseButtonsUpdate = { |buttonIndex|\r\n        if(phases[buttonIndex] == 0.0 || (phases[buttonIndex] == 180.0)) {\r\n            phaseButtons[buttonIndex]\r\n            .states_([[\"O\", Color.black], [\"Ø\", Color.green(0.6)]]);\r\n            phaseButtons[buttonIndex].value = (phases[buttonIndex] % 180)\r\n        } {\r\n            phaseButtons[buttonIndex]\r\n            .states_([[\"↓\", Color.blue(1, 0.5)]])\r\n        }\r\n    };\r\n\r\n    lable.(win, 10, upperHeight + 3, 300, 20, \\left, 12,\r\n        \"Base frequency (Hz):                  (test with 393.44)\",\r\n        Color.black, Color.clear);\r\n\r\n    freqBox = NumberBox(win, Rect(130, upperHeight + 3, 50, 20))\r\n    .font_(Font(\"Arial\", 12));\r\n\r\n    freqBox.value_(~freq)\r\n    .action_{ |numb|\r\n        var freqBase = numb.value;\r\n        x.set(\\freq, numb.value);\r\n        waveform.cycle_(s.sampleRate / numb.value);\r\n        antiAliasing.(freqBase)\r\n    };\r\n\r\n    freqScope = FreqScopeView(win, Rect(2, upperHeight + 3 + 22, 511, lowerHeight - 28));\r\n    freqScope.active_(true);\r\n\r\n    ListView(win, Rect(2 + 511 - 30, upperHeight + 3 + 22, 30, 34))\r\n    .items_([\"lin\", \"log\"])\r\n    .action_{ |menu|\r\n        freqScope.freqMode_(menu.value);\r\n    };\r\n\r\n    ampNum = NumberBox(win, Rect(3 + 511, upperHeight + 3, 29, 20))\r\n    .font_(Font(\"Arial\", 12))\r\n    .value_(0.1)\r\n    .action_{ |numb|\r\n        ampSlider.value_(numb.value);\r\n        x.set(\\mul, numb.value);\r\n    };\r\n\r\n    ampSlider = Slider(win, Rect(2 + 511, upperHeight + 2 + 22, 31, lowerHeight - 26))\r\n    .value_(~mul)\r\n    .action_{\r\n        x.set(\\mul, ampSlider.value);\r\n        ampNum.value_(ampSlider.value);\r\n    };\r\n\r\n    ServerMeterView(s, win, (2 + 511 + 30) @ (upperHeight + 18), 0, 2);\r\n\r\n    waveformView = View(win, Rect(\r\n        2 + 511 + 30 + ServerMeterView.getWidth(0, 1, s),\r\n        upperHeight + 1,\r\n        255,\r\n        255\r\n    ));\r\n    waveform = Stethoscope(s, 1, view: waveformView);\r\n\r\n    SynthDef(\\overtone_additive, { |overtoneFirst = 1, overtoneLast = 1, numNotZero = nil|\r\n        var freq, phases, mul, numOvertones, maxLayers, amplitudes, temp, signalArray, sum;\r\n        mul = \\mul.kr(~mul);\r\n        freq = \\freq.kr(~freq);\r\n        maxLayers = ~numSliders;\r\n        phases = \\phases.kr(0 ! maxLayers);\r\n        amplitudes = \\amplitudes.kr(0 ! maxLayers);\r\n        numOvertones = if(numNotZero == nil) {\r\n            overtoneLast - overtoneFirst + 1\r\n        } {\r\n            numNotZero - overtoneFirst + 1\r\n        }.clip(1, ~numSliders);\r\n        signalArray = maxLayers.collect { |i|\r\n            var nthOvertone, minDetect, maxDetect, aliasingDetect, switch;\r\n            nthOvertone = i + 1 * freq;\r\n            minDetect = overtoneFirst <= (i + 1);\r\n            maxDetect = overtoneLast >= (i + 1);\r\n            aliasingDetect = nthOvertone < (SampleRate.ir / 2);\r\n            switch = (minDetect * maxDetect * aliasingDetect).lag(0.2);\r\n            SinOsc.ar(nthOvertone, phases[i].lag(0.2)) * switch * numOvertones.reciprocal.sqrt.lag(0.2)\r\n        };\r\n        signalArray = signalArray * amplitudes.lag(0.2);\r\n        sum = signalArray.sum * mul.lag(0.2);\r\n        Out.ar(0, sum ! 2);\r\n    }).add;\r\n\r\n    s.sync;\r\n\r\n    waveform.cycle_(s.sampleRate / ~freq);\r\n    x = Synth(\\overtone_additive);\r\n}\r\n)\r\n\r\nx = Synth(\\overtone_additive, [\\amplitudes, 1 ! ~numSliders, \\mul, 0.1]);\r\nx.set(\\overtoneFirst, 2, \\overtoneLast, 2);\r\nx.set(\\overtoneFirst, 1, \\overtoneLast, 1);\r\nx.set(\\overtoneFirst, 1, \\overtoneLast, 2);\r\nx.set(\\overtoneFirst, 1, \\overtoneLast, 3);\r\nx.set(\\overtoneFirst, 4, \\overtoneLast, 7);\r\nx.free",
   "labels" : [
      "additive",
      "partial",
      "overtones"
   ],
   "ancestor_list" : [],
   "description" : "[related forum post 1](https://scsynth.org/t/how-to-avoid-exception-in-graphdef-load-exceeded-number-of-interconnect-buffers-how-to-make-certain-sliders-of-multislider-read-only/8827/3)\r\n\r\n[related forum post 2](https://scsynth.org/t/newbie-cannot-get-new-code-to-compile/8840/3?u=prko)\r\n\r\n[related forum post 3](https://scsynth.org/t/gui-interactivity-limitations-or-the-problem-of-the-code-itself/8897)",
   "author" : "prko",
   "name" : "Overtone Explorer"
}
