aboutsummaryrefslogtreecommitdiff
path: root/site/app/feedback/index.html
diff options
context:
space:
mode:
authorRichard <q@1bpm.net>2025-04-13 18:48:02 +0100
committerRichard <q@1bpm.net>2025-04-13 18:48:02 +0100
commit9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 (patch)
tree291bd79ce340e67affa755a8a6b4f6a83cce93ea /site/app/feedback/index.html
downloadapps.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/index.html')
-rw-r--r--site/app/feedback/index.html496
1 files changed, 496 insertions, 0 deletions
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>