diff options
author | Richard <q@1bpm.net> | 2025-04-13 18:48:02 +0100 |
---|---|---|
committer | Richard <q@1bpm.net> | 2025-04-13 18:48:02 +0100 |
commit | 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 (patch) | |
tree | 291bd79ce340e67affa755a8a6b4f6a83cce93ea /site/app/feedback | |
download | apps.csound.1bpm.net-9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22.tar.gz apps.csound.1bpm.net-9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22.tar.bz2 apps.csound.1bpm.net-9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22.zip |
initial
Diffstat (limited to 'site/app/feedback')
-rw-r--r-- | site/app/feedback/feedback.csd | 195 | ||||
-rw-r--r-- | site/app/feedback/index.html | 496 |
2 files changed, 691 insertions, 0 deletions
diff --git a/site/app/feedback/feedback.csd b/site/app/feedback/feedback.csd new file mode 100644 index 0000000..4a8a7f9 --- /dev/null +++ b/site/app/feedback/feedback.csd @@ -0,0 +1,195 @@ +<CsoundSynthesizer>
+<CsOptions>
+-odac
+</CsOptions>
+<CsInstruments>
+sr = 44100
+ksmps = 16
+nchnls = 2
+0dbfs = 2
+seed 0
+
+
+#include "wavetables.udo"
+#include "frequency_tools.udo"
+
+gifbm_maxgain = 2
+
+opcode fbm_param, k, Sijjp
+ Sname, ichannel, imin, imax, iapplyportamento xin
+ Schannel = sprintf("%s_%d", Sname, ichannel)
+ krawval chnget Schannel
+ if (iapplyportamento == 1) then
+ kval port krawval, 0.005
+ else
+ kval = krawval
+ endif
+ SmodOnChannel = sprintf("%smodulating", Schannel)
+ kmodulating chnget SmodOnChannel
+ if (kmodulating == 1) then
+ kmodwave = chnget:k(sprintf("%smodwave", Schannel))
+ kmodfreq = chnget:k(sprintf("%smodfreq", Schannel))
+ kmodamp = chnget:k(sprintf("%smodamp", Schannel))
+ kmod = abs:k(oscilikt:k(kmodamp, kmodfreq, giwavetables[kmodwave]))
+ kout = kval + (kmod * (imax - imin))
+ else
+ kout = kval
+ endif
+/*
+ if (imin != -1) then
+ chnset imin, Schannel ; safe set channel to minimum
+ kout = max:k(kout, imin)
+ endif
+
+ if (imax != -1) then
+ kout = min:k(kout, imax)
+ endif
+*/
+ xout kout
+endop
+
+
+opcode fbm_fxinsert, a, aii
+ asig, ichanindex, insertindex xin
+ SchanAppend = sprintf("_%d_%d", ichanindex, insertindex)
+ keffect = chnget:k(strcat("insert", SchanAppend))
+ if (keffect == 1) then
+ asig freqshift1 asig, chnget:k(strcat("fs_freq", SchanAppend))
+ elseif (keffect == 2) then
+ asig ringmod1 asig, chnget:k(strcat("rm_freq", SchanAppend))
+ elseif (keffect == 3) then
+ asig nreverb asig, chnget:k(strcat("rv_time", SchanAppend)), 0.5
+ elseif (keffect == 4) then
+
+ elseif (keffect == 5) then
+
+ endif
+ xout asig
+endop
+
+
+opcode fbm_channel, aaaaa, ai
+ asig, ichannel xin
+ ieqgainadd = 4
+ keqh_gain = fbm_param("eqhighgain", ichannel, 0, gifbm_maxgain)
+ keqm_gain = fbm_param("eqmidgain", ichannel, 0, gifbm_maxgain)
+ keql_gain = fbm_param("eqlowgain", ichannel, 0, gifbm_maxgain)
+
+ keqh_freq = fbm_param("eqhighfreq", ichannel, 20, 18000)
+ keqm_freq = fbm_param("eqmidfreq", ichannel, 20, 18000)
+ keql_freq = fbm_param("eqlowfreq", ichannel, 20, 18000)
+
+ keqh_q = fbm_param("eqhighq", ichannel, 0.71, 0.9)
+ keqm_q = fbm_param("eqmidq", ichannel, 0.71, 0.9)
+ keql_q = fbm_param("eqlowq", ichannel, 0.71, 0.9)
+
+ ksend1 = fbm_param("send1", ichannel, 0, gifbm_maxgain, 0)
+ ksend2 = fbm_param("send2", ichannel, 0, gifbm_maxgain, 0)
+ ksend3 = fbm_param("send3", ichannel, 0, gifbm_maxgain, 0)
+ ksend4 = fbm_param("send4", ichannel, 0, gifbm_maxgain, 0)
+
+ kprefade1 = fbm_param("prefade1", ichannel, 0, 1, 0)
+ kprefade2 = fbm_param("prefade2", ichannel, 0, 1, 0)
+ kprefade3 = fbm_param("prefade3", ichannel, 0, 1, 0)
+ kprefade4 = fbm_param("prefade4", ichannel, 0, 1, 0)
+
+ klowcut = fbm_param("lowcut", ichannel, 0, 1, 0)
+ kmute = fbm_param("mute", ichannel, 0, 1, 0)
+ kvolume = fbm_param("volume", ichannel, 0, gifbm_maxgain)
+
+ asig dcblock asig
+ asig += noise(0.01, 0)
+ /*
+ asig pareq asig, keql_freq, keql_gain * ieqgainadd, keql_q, 1, 1
+ asig pareq asig, keqm_freq, keqm_gain * ieqgainadd, keqm_q, 0, 1
+ asig pareq asig, keqh_freq, keqh_gain * ieqgainadd, keqh_q, 2, 1
+ */
+ asig pareq asig, keql_freq, keql_gain * ieqgainadd, 0.7, 1
+ asig pareq asig, keqm_freq, keqm_gain * ieqgainadd, 0.7, 0
+ asig pareq asig, keqh_freq, keqh_gain * ieqgainadd, 0.7, 2
+ asig pareq asig, keqh_freq, keqh_gain * ieqgainadd, 0.7, 2
+
+ if (klowcut == 1) then
+ asig butterhp asig, 75
+ endif
+ asig butterlp asig, 17000
+ asig butterhp asig, 0.1
+
+ ;asig dam asig, 0.99, 0.9, 0.9, 0.01, 0.01
+
+ kvolume *= (1 - kmute)
+ asend1 = tanh(asig * ((kprefade1 == 1) ? ksend1 : ksend1 * kvolume))
+ asend2 = tanh(asig * ((kprefade2 == 1) ? ksend2 : ksend2 * kvolume))
+ asend3 = tanh(asig * ((kprefade3 == 1) ? ksend3 : ksend3 * kvolume))
+ asend4 = tanh(asig * ((kprefade4 == 1) ? ksend4 : ksend4 * kvolume))
+
+
+ asig fbm_fxinsert asig, ichannel, 0
+ asig fbm_fxinsert asig, ichannel, 1
+
+ asig tanh asig
+ asig *= kvolume
+ xout asig, asend1, asend2, asend3, asend4
+endop
+
+
+
+
+instr mixer
+ a1 init 0
+ a2 init 0
+ a3 init 0
+ a4 init 0
+
+ aout1, a1s1, a1s2, a1s3, a1s4 fbm_channel a1, 0
+ aout2, a2s1, a2s2, a2s3, a2s4 fbm_channel a2, 1
+ aout3, a3s1, a3s2, a3s3, a3s4 fbm_channel a3, 2
+ aout4, a4s1, a4s2, a4s3, a4s4 fbm_channel a4, 3
+ a1 = 0
+ a2 = 0
+ a3 = 0
+ a4 = 0
+
+ icrosstalk = 0.0001
+
+ a1 = a1s1 + a2s1 + a3s1 + a4s1
+ a2 = a1s2 + a2s2 + a3s2 + a4s2
+ a3 = a1s3 + a2s3 + a3s3 + a4s3
+ a4 = a1s4 + a2s4 + a3s4 + a4s4
+
+ a1 = a1 + (a2 * icrosstalk)
+ a2 = a2 + (a1 * icrosstalk) + (a3 * icrosstalk)
+ a3 = a3 + (a3 * icrosstalk) + (a4 * icrosstalk)
+ a4 = a4 + (a3 * icrosstalk)
+ aout = aout1 + aout2 + aout3 + aout4
+ aout tanh aout
+ aout pareq aout, 18000, 0.4, 0.7
+ aout dcblock aout * 0.25
+ aout butterlp aout, 17000
+ aout limit aout, -1, 1
+ outs aout, aout
+
+#ifndef WEB
+ chnset 0.01, "eqhighgain_0"
+ chnset 10000, "eqhighfreq_0"
+ chnset 0.71, "eqhighq_0"
+
+ chnset 3.7, "eqmidgain_0"
+ chnset 1100, "eqmidfreq_0"
+ chnset 0.71, "eqmidq_0"
+
+ chnset 6.4, "eqlowgain_0"
+ chnset 550, "eqlowfreq_0"
+ chnset 0.71, "eqlowq_0"
+
+ chnset 1, "prefade1_0"
+ chnset 1.1, "send1_0"
+ chnset 1.1, "volume_0"
+#end
+endin
+
+</CsInstruments>
+<CsScore>
+i"mixer" 0 36000
+</CsScore>
+</CsoundSynthesizer>
\ No newline at end of file diff --git a/site/app/feedback/index.html b/site/app/feedback/index.html new file mode 100644 index 0000000..978b8b7 --- /dev/null +++ b/site/app/feedback/index.html @@ -0,0 +1,496 @@ +<html> + <head> + <script type="text/javascript" src="/code/jquery.js"></script> + <script type="text/javascript" src="../base/base.js"></script> + <script type="text/javascript" src="/code/input-knobs.js"></script> + <style type="text/css"> + body { + font-family: Sans-serif, arial; + font-size: 8pt; + background-color: #989898; + } + + td { + font-size: 8pt; + } + + .controlLabel { + font-size: 8pt; + text-align: center; + width: 100%; + } + + .controlValue { + font-size: 7pt; + text-align: center; + width: 100%; + } + + table { + border-spacing: 0; + } + + .innertd { + border-left: 1px solid #787878; + padding: 1px; + } + + .chantd { + border-left: 2px solid #454545; + padding: 1px; + } + + .smbutton { + font-size: 7pt; + border: none; + padding: 1px; + } + + #buttons { + position: absolute; + top: 0px; + left: 0px; + height: 5%; + width: 80%; + } + + #target { + position: absolute; + top: 5%; + left: 0px; + width: 80%; + height: 95px; + } + + #modulation { + position: absolute; + top: 0%; + left: 70%; + width: 30%; + padding: 10px; + height: 100%; + border-left: 1px solid black; + } + + #modulatehelp { + display: none; + } + + #begin { + position: fixed; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + background-color: #b5b5b5; + cursor: pointer; + z-index: 10; + } + + #begininner { + position: absolute; + text-align: center; + top: 20%; + left: 20%; + width: 60%; + font-size: 11pt; + } + + #loading { + position: fixed; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + background-color: #b5b5b5; + cursor: pointer; + display: none; + z-index: 11; + } + + #loadinginner { + position: absolute; + text-align: center; + top: 20%; + left: 20%; + width: 60%; + font-size: 24pt; + } + + + </style> + <script type="text/javascript"> + var baseurl = "/controls/"; + var maxGain = 2; + var controls = []; + + function resetControls() { + for (let cn of controls) { + var c = cn.control; + var val = cn.dfault; + if (val) { + if (c.attr("type") == "checkbox") { + c.prop("checked", (val == 1)); + } else { + c.val(val); + } + } + c.trigger("change"); + } + } + + function randomiseControls() { + for (let cn of controls) { + var c = cn.control; + var min = parseFloat(c.attr("min")); + var max = parseFloat(c.attr("max")); + var step = parseFloat(c.attr("step")); + var val = (Math.random() * (max - min)) + min; + if (val % step != 0) { + if (step == 1) { + val = Math.round(val); + } else { + val = Math.ceil((val - min) / step) * step + min; + } + } + if (c.attr("type") == "checkbox") { + c.prop("checked", (val == 1)); + } else { + c.val(val); + } + c.trigger("change"); + } + } + + var randomiseTimeout; + var randomising = false; + function randomiseContinuous() { + var text; + if (randomising) { + randomising = false; + text = "Continuous random"; + clearTimeout(randomiseTimeout); + } else { + randomising = true; + text = "Stop"; + function doRandom() { + randomiseControls(); + if (randomising) { + setTimeout(doRandom, Math.round(Math.random() * 700) + 50); + } + } + doRandom(); + } + $("#randomiseContinuous").text(text); + } + + async function makeRow(rowdef) { + var row = $("<tr />"); + var showName = true; + for (let chan = 0; chan < 4; chan++) { // channel + var initial = true; + var showBorder = true; + var sectionName = null; + for (let def of rowdef) { + let elValue; + var className; + + if (!showName && initial) { + initial = false; + continue; + } + + if (showName) { + sectionName = def; + initial = false; + showName = false; + } + + if (showBorder) { + className = "chantd"; + showBorder = false; + } else { + className = "innertd"; + } + + var el = $("<td />").appendTo(row).addClass(className); + if (!def) continue; + + if (typeof(def) == "string") { + el.text(def); + } else { + var control = await getControl(def.control); + el.append(control); + var name = "Channel " + (chan + 1) + " " + ((sectionName) ? sectionName + " " : "") + def.name; + var dfault = (def.dfault) ? def.dfault: 0; + var min = (def.min) ? def.min: 0; + var max = (def.max) ? def.max: 1; + var step = (def.step) ? def.step: 0.000001; + controls.push({name: name, control: control, channel: def.channel + "_" + chan, dfault: dfault}); + + setTimeout(function() { + app.setControlChannel(def.channel + "_" + chan, (def.dfault) ? def.dfault: 0); + }, 100); + if (def.size) { + el.width(def.size).height(def.size); + } + + if (def.type && def.type == "button") { + step = 1; + } + + function updateControl() { + var val; + if ($(this).attr("type") == "checkbox") { + val = ($(this).is(":checked")) ? 1 : 0; + } else { + val = $(this).val(); + } + app.setControlChannel(def.channel + "_" + chan, val); + if (elValue) { + elValue.text(Math.round(val * 100) / 100); + } + } + + control.attr("step", step).attr("min", min).attr("max", max).val(dfault).on("input", updateControl).change(updateControl); + if (def.name && !def.noLabel) { + var elLabel = $("<div />").addClass("controlLabel").text(def.name); + el.append(elLabel); + } + if (!def.noValue) { + elValue = $("<div />").addClass("controlValue").text(dfault); + el.append(elValue); + } + } + } + } + return row; + } + + var modulationParameters = [ + {name: "Wave", channel: "modwave", options: ["Sine", "Square", "Saw", "Pulse", "Cosine", "Triangle"], dfault: 0}, + {name: "Frequency", channel: "modfreq", dfault: 1, min: 0.01, max: 10, step: 0.01}, + {name: "Amplitude", channel: "modamp", dfault: 1, min: 0, max: 1, step: 0.000001} + ]; + + + modulations = []; + + function createModulation(c) { + var tbl = $("<table />").css("width", "100%"); + var tb = $("<tbody />").appendTo(tbl); + var el = $("<div />").appendTo($("#modulationsData")); + var offButton = $("<button />").text("Remove").click(function() { + delete modulations[modulations.indexOf(c)]; + el.remove(); + app.setControlChannel(c.channel + "modulating", 0); + }); + app.setControlChannel(c.channel + "modulating", 1); + + el.append($("<hr />")).append(c.name).append(tbl).append(offButton); + var dfault; + for (let m of modulationParameters) { + var tr = $("<tr />").appendTo(tb); + tr.append($("<td />").text(m.name)); + var elInput; + if (m.options) { + elInput = $("<select />").change(function() { + app.setControlChannel(c.channel + m.channel, $(this).val()); + }); + for (var i in m.options) { + elInput.append($("<option />").text(m.options[i]).val(i)); + } + + dfault = (m.dfault) ? m.dfault : 0; + elInput.val(dfault).trigger("change"); + } else { + elInput = $("<input />").attr("type", "range").attr("min", m.min).attr("max", m.max).attr("step", m.step).val(m.dfault).on("input", function() { + app.setControlChannel(c.channel + m.channel, $(this).val()); + }); + elInput.trigger("input"); + dfault = m.dfault; + } + app.setControlChannel(c.channel + m.channel, dfault); + + $("<td />").append(elInput).appendTo(tr); + + } + } + + + var modulationsShown = false; + function modulateControls() { + var elBut = $("#modulate"); + var elHelp = $("#modulatehelp"); + + function done() { + elBut.text("Add new"); + elHelp.hide(); + modulationsShown = false; + for (let c of controls) { + c.control.css("opacity", 1).off("click").off("touchstart"); + } + } + + if (modulationsShown) { + done(); + } else { + elHelp.show(); + elBut.text("Exit assignment"); + modulationsShown = true; + for (let c of controls) { + function onSelect() { + if (!modulations.includes(c)) { + modulations.push(c); + done(); + createModulation(c); + } + } + c.control.css("opacity", 0.2).click(onSelect).on("touchstart", onSelect); + } + } + + } + + + async function makeTable() { + var tb = $("<tbody />").appendTo($("<table />").addClass("mainTable").appendTo($("#target"))); + var def = [ + ["EQ high", + {name: "Gain", channel: "eqhighgain", control: "timb_SM2018_SM_CUTE32-1", dfault: 0.7, min: 0.1, max: 1.1}, + {name: "Frequency", channel: "eqhighfreq", control: "timb_SM2018_SM_CUTE32-1", dfault: 12000, step: 1, min: 6000, max: 20000}, + {name: "Q", channel: "eqhighq", control: "timb_SM2018_SM_CUTE32-1", dfault: 0.71, min: 0.71, max: 0.9} + ], + ["EQ mid", + {name: "Gain", channel: "eqmidgain", control: "timb_SM2018_SM_CUTE32-1", dfault: 1, min: 0.01, max: maxGain}, + {name: "Frequency", channel: "eqmidfreq", control: "timb_SM2018_SM_CUTE32-1", dfault: 2000, step: 1, min: 800, max: 6000}, + {name: "Q", channel: "eqmidq", control: "timb_SM2018_SM_CUTE32-1", dfault: 0.71, min: 0.71, max: 0.9} + ], + ["EQ low", + {name: "Gain", channel: "eqlowgain", control: "timb_SM2018_SM_CUTE32-1", dfault: 1, min: 0.01, max: maxGain}, + {name: "Frequency", channel: "eqlowfreq", control: "timb_SM2018_SM_CUTE32-1", dfault: 200, step: 1, min: 50, max: 800}, + {name: "Q", channel: "eqlowq", control: "timb_SM2018_SM_CUTE32-1", dfault: 0.71, min: 0.71, max: 0.9} + ], + ["Prefade", + {name: "1", channel: "prefade1", control: "Timb_Grig2018_Controls--61b", type: "button", size: 12, noValue: true, noLabel: true}, + {name: "2", channel: "prefade2", control: "Timb_Grig2018_Controls--61b", type: "button", size: 12, noValue: true, noLabel: true}, + null + ], + ["Send", + {name: "1", channel: "send1", control: "timb_SM2018_SM_CUTE32-4", max: maxGain}, + {name: "2", channel: "send2", control: "timb_SM2018_SM_CUTE32-4", max: maxGain}, + null + ], + ["Prefade", + {name: "3", channel: "prefade3", control: "Timb_Grig2018_Controls--61b", type: "button", size: 12, noValue: true, noLabel: true}, + {name: "4", channel: "prefade4", control: "Timb_Grig2018_Controls--61b", type: "button", size: 12, noValue: true, noLabel: true}, + null + ], + ["Send", + {name: "3", channel: "send3", control: "timb_SM2018_SM_CUTE32-4", max: maxGain}, + {name: "4", channel: "send4", control: "timb_SM2018_SM_CUTE32-4", max: maxGain}, + null + ], + [null, + {name: "Mute", channel: "mute", control: "timb&HYRPEMUTE32", type: "button", noValue: true}, + {name: "Low cut", channel: "lowcut", control: "Timb_Grig2018_Controls--61b", type: "button", noValue: true}, + null + ], + [null, + null, + {name: "Volume", channel: "volume", control: "lbx_slider160_smoothblack_red_km", max: maxGain}, + null + ] + ]; + + + for (let d of def) { + var row = await makeRow(d); + tb.append(row); + }; + } + + + async function appendRandomControl() { + var all = await fetch("/controls/all.json"); + var json = await all.json(); + var item = json[Object.keys(json)[Math.round(Math.random() * (Object.keys(json).length - 1))]]; + var ctrl = await getControl(item); + $("#target").append(ctrl); + } + + async function getControl(name) { + if (typeof(name) == "string") { + var response = await fetch(baseurl + name + ".json"); + var json = await response.json(); + } else { + json = name; // for randomControl + } + + var element; + if (json.ctltype == 0) { + element = $("<input />").attr("type", "range").addClass("input-knob").attr("data-diameter", json.cellh).attr("data-src", baseurl + json.fn).attr("data-sprites", json.frames - 1); + } else if (json.ctltype == 1) { + element = $("<input />").attr("type", "range").addClass("input-slider").attr("data-height", json.cellh).attr("data-src", baseurl + json.fn).attr("data-width", json.cellw).attr("data-sprites", json.frames - 1); + } else if (json.ctltype == 2) { + element = $("<input />").attr("type", "checkbox").addClass("input-switch").attr("data-height", json.cellh).attr("data-width", json.cellw).attr("data-src", baseurl + json.fn); + } + return element; + } + + $(function(){ + makeTable(); + + + window.app = new CSApplication({ + csdUrl: "feedback.csd", + onPlay: function () { + $("#loading").hide(); + randomiseControls(); + resetControls(); + } + }); + + $("#randomise").click(randomiseControls); + $("#randomiseContinuous").click(randomiseContinuous); + $("#reset").click(resetControls); + $("#modulate").click(modulateControls); + + $("#begin").click(function() { + $("#begin").hide(); + $("#loading").show(); + app.play(); + }); + }); + </script> + </head> + <body> + <div id="begin"> + <div id="begininner"> + <h3>Feedback mixer simulation</h3> + <p>This application models a four channel audio mixer in a fed-back state. Often known as a 'no-input' mixer, + eccentric sounds can be induced from altering EQ and gain in the channel paths.<br /> + In this model, there are four auxilliary sends on each channel, which send input to the corresponding channel.<br /> + For example, increasing aux 1 on channel 1 will eventually lead to feedback. The randomise buttons at the top can + be used for quick shortcuts for creating sound, and using the modulations pane on the left allows any parameter to be modulated with a LFO. + </p> + <h2>Press to begin</h2> + </div> + </div> + <div id="loading"><div id="loadinginner">Loading audio engine</div></div> + <div id="buttons"> + <table><tbody><tr> + <td><button id="randomise">Randomise</button></td> + <td><button id="randomiseContinuous">Continuous random</button></td> + <!--<td><button id="reset">Reset</button></td>--> + </tr></tbody></table> + </div> + <div id="target"></div> + <div id="modulation"> + <h3>Modulations</h3> + <td><button id="modulate">Add new</button></td> + <div id="modulatehelp">Click on a parameter to enable modulation</div> + <div id="modulationsData"></div> + </div> + </body> +</html> |