From c8c410a38f8ecab7df6753c0c60d92708b02a04d Mon Sep 17 00:00:00 2001 From: trian-gles <69212477+trian-gles@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:31:57 +0200 Subject: [PATCH 1/7] UI changes --- example.maxpat | 6 +++--- lfogui.js | 12 ++++++++++-- modulators.js | 21 ++++++++++++++------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/example.maxpat b/example.maxpat index 1e5b07d..2b56a7d 100644 --- a/example.maxpat +++ b/example.maxpat @@ -130,7 +130,7 @@ "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 586.0, 713.0, 89.0, 36.0 ], - "text" : "harmoniclarity 40.156651" + "text" : "harmoniclarity 14.213599" } } @@ -143,7 +143,7 @@ "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 491.0, 713.0, 85.0, 36.0 ], - "text" : "event_length 29.843349" + "text" : "event_length 55.786401" } } @@ -156,7 +156,7 @@ "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 385.0, 715.0, 84.0, 36.0 ], - "text" : "metriclarity 22.176645" + "text" : "metriclarity 79.192955" } } diff --git a/lfogui.js b/lfogui.js index 29946c1..ec49f43 100644 --- a/lfogui.js +++ b/lfogui.js @@ -48,6 +48,7 @@ function MasterLfoHandler(){ const [userDefinedWave, setUserDefinedWave] = React.useState(Array(50).fill(0)); const [modVisibleArr, setModVisibleArr] = React.useState(initVisArr); + const [modTypeArr, setModTypeArr] = React.useState(Array(MAXLFOS).fill('LFO')); const [modInstanceNumArr, setModInstanceNumArr] = React.useState(Array(MAXLFOS).fill('1')); const [modCenterVals, setModCenterVals] = React.useState({'1':{}, '2':{}, '3':{}, '4':{}}); @@ -55,6 +56,7 @@ function MasterLfoHandler(){ const [bpm, setBpm] = React.useState(100); const [beatsInMeasure, setBeatsInMeasure] = React.useState(4); + const [noiseTypeArr, setNoiseTypeArr] = React.useState(Array(MAXLFOS).fill('Sine Int.')); const [shapeArr, setShapeArr] = React.useState(Array(MAXLFOS).fill('Sine')); const [djParamArr, setDjParamArr] = React.useState(Array(MAXLFOS).fill('NONE')); @@ -66,8 +68,8 @@ function MasterLfoHandler(){ const [phaseArr, setPhaseArr] = React.useState(Array(MAXLFOS).fill('0')); - const allModArrays = [modVisibleArr, modInstanceNumArr, shapeArr, djParamArr, freqArr, minArr, maxArr, phaseArr]; - const allModSetters = [setModVisibleArr, setModInstanceNumArr, setShapeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setPhaseArr]; + const allModArrays = [modVisibleArr, modTypeArr, modInstanceNumArr, shapeArr, noiseTypeArr, djParamArr, freqArr, minArr, maxArr, phaseArr]; + const allModSetters = [setModVisibleArr, setModTypeArr, setModInstanceNumArr, setShapeArr, setNoiseTypeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setPhaseArr]; const modBlankVals = [true, '1', SHAPETYPES[0], MODPARAMOPTIONS[0], '1hz', '0', '1', '0']; @@ -258,8 +260,14 @@ function MasterLfoHandler(){ e(LfoRow, { instanceNum : modInstanceNumArr[i], setInstanceNum: CreateParamChanger(modInstanceNumArr, setModInstanceNumArr, i), + + type: modTypeArr[i], + setType: CreateParamChanger(modTypeArr, setModTypeArr, i), shape: shapeArr[i], setShape: CreateParamChanger(shapeArr, setShapeArr, i), + + noise: noiseTypeArr[i], + setNoise: CreateParamChanger(noiseTypeArr, setNoiseTypeArr, i), djParam: djParamArr[i], setDjParam: CreateParamChanger(djParamArr, setDjParamArr, i), centerVals: modCenterVals, diff --git a/modulators.js b/modulators.js index 483c889..4905221 100644 --- a/modulators.js +++ b/modulators.js @@ -2,7 +2,10 @@ // MODULATORS ///////////////////////// + +var TYPEOPTIONS = ["LFO", "Noise"]; var SHAPETYPES = ["Sine", "SawUp", "SawDown", "Tri", "Square", "Custom"]; +var NOISETYPES = ["Sine Int."] var INSTANCEOPTIONS = ["1", "2", "3", "4"]; @@ -10,11 +13,6 @@ const MODPARAMOPTIONS = ["NONE", "stream", "pulse_length", "eventfulness", "even "harmoniclarity", "melodic_cohesion", "melody_scope", "tonic_pitch", "pitch_center", "pitch_range", "dynamics", "attenuation", "chordal_weight", "tonality-profile", "ostinato-buffer", "ostinato", "meter", "scale"]; -function ControlType(){ - return e('select', {className: 'control-type'}, Option("LFO")); -} - - function LfoRow(props){ @@ -23,10 +21,19 @@ function LfoRow(props){ if (!center) center = 0; + let typeOption = null; + + if (props.type == "LFO"){ + typeOption = ListItem(DropDown({onChange: props.setShape, value:props.shape, options: SHAPETYPES})); + } + else if (props.type == "Noise"){ + typeOption = ListItem(DropDown({onChange: props.setNoise, value:props.noise, options: NOISETYPES})); + } + let content = e('ul', {className: 'lfo-item'}, ListItem(DropDown({onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), - ListItem(ControlType()), - ListItem(DropDown({onChange: props.setShape, value:props.shape, options: SHAPETYPES})), + ListItem(DropDown({options: TYPEOPTIONS, onChange: props.setType, value:props.type})), + typeOption, ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), ListItem(e("input", {onChange:props.setFreq, value:props.freq, className:"timeInput"}, null)), ListItem(e(NumberBox, {onChange:props.setMin, value:props.min, step:0.1}, null)), From 45e168ba1133490cadffe82e9babbfdddb3fc2ba Mon Sep 17 00:00:00 2001 From: trian-gles <69212477+trian-gles@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:31:57 +0200 Subject: [PATCH 2/7] UI changes --- example.maxpat | 6 +++--- lfogui.js | 12 ++++++++++-- modulators.js | 16 ++++++++++++++-- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/example.maxpat b/example.maxpat index 2619f50..de201ed 100644 --- a/example.maxpat +++ b/example.maxpat @@ -168,7 +168,7 @@ "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 586.0, 713.0, 89.0, 36.0 ], - "text" : "harmoniclarity 40.156651" + "text" : "harmoniclarity 14.213599" } } @@ -181,7 +181,7 @@ "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 491.0, 713.0, 85.0, 36.0 ], - "text" : "event_length 29.843349" + "text" : "event_length 55.786401" } } @@ -194,7 +194,7 @@ "numoutlets" : 1, "outlettype" : [ "" ], "patching_rect" : [ 385.0, 715.0, 84.0, 36.0 ], - "text" : "metriclarity 0.5" + "text" : "metriclarity 79.192955" } } diff --git a/lfogui.js b/lfogui.js index 24281fa..7dc65bc 100644 --- a/lfogui.js +++ b/lfogui.js @@ -48,6 +48,7 @@ function MasterLfoHandler(){ const [userDefinedWave, setUserDefinedWave] = React.useState(Array(50).fill(0)); const [modVisibleArr, setModVisibleArr] = React.useState(initVisArr); + const [modTypeArr, setModTypeArr] = React.useState(Array(MAXLFOS).fill('LFO')); const [modInstanceNumArr, setModInstanceNumArr] = React.useState(Array(MAXLFOS).fill('1')); const [modCenterVals, setModCenterVals] = React.useState({'1':{}, '2':{}, '3':{}, '4':{}}); @@ -55,6 +56,7 @@ function MasterLfoHandler(){ const [ticks, setTicks] = React.useState(0); const [beatsInMeasure, setBeatsInMeasure] = React.useState(4); + const [noiseTypeArr, setNoiseTypeArr] = React.useState(Array(MAXLFOS).fill('Sine Int.')); const [shapeArr, setShapeArr] = React.useState(Array(MAXLFOS).fill('Sine')); const [djParamArr, setDjParamArr] = React.useState(Array(MAXLFOS).fill('NONE')); @@ -66,8 +68,8 @@ function MasterLfoHandler(){ const [phaseArr, setPhaseArr] = React.useState(Array(MAXLFOS).fill('0')); - const allModArrays = [modVisibleArr, modInstanceNumArr, shapeArr, djParamArr, freqArr, minArr, maxArr, phaseArr]; - const allModSetters = [setModVisibleArr, setModInstanceNumArr, setShapeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setPhaseArr]; + const allModArrays = [modVisibleArr, modTypeArr, modInstanceNumArr, shapeArr, noiseTypeArr, djParamArr, freqArr, minArr, maxArr, phaseArr]; + const allModSetters = [setModVisibleArr, setModTypeArr, setModInstanceNumArr, setShapeArr, setNoiseTypeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setPhaseArr]; const modBlankVals = [true, '1', SHAPETYPES[0], MODPARAMOPTIONS[0], '1hz', '0', '1', '0']; @@ -259,8 +261,14 @@ function MasterLfoHandler(){ e(LfoRow, { instanceNum : modInstanceNumArr[i], setInstanceNum: CreateParamChanger(modInstanceNumArr, setModInstanceNumArr, i), + + type: modTypeArr[i], + setType: CreateParamChanger(modTypeArr, setModTypeArr, i), shape: shapeArr[i], setShape: CreateParamChanger(shapeArr, setShapeArr, i), + + noise: noiseTypeArr[i], + setNoise: CreateParamChanger(noiseTypeArr, setNoiseTypeArr, i), djParam: djParamArr[i], setDjParam: CreateParamChanger(djParamArr, setDjParamArr, i), centerVals: modCenterVals, diff --git a/modulators.js b/modulators.js index dc4a778..0f33b22 100644 --- a/modulators.js +++ b/modulators.js @@ -2,7 +2,10 @@ // MODULATORS ///////////////////////// + +var TYPEOPTIONS = ["LFO", "Noise"]; var SHAPETYPES = ["Sine", "SawUp", "SawDown", "Tri", "Square", "Custom"]; +var NOISETYPES = ["Sine Int."] var INSTANCEOPTIONS = ["1", "2", "3", "4"]; @@ -28,10 +31,19 @@ function LfoRow(props){ if (!center) center = 0; + let typeOption = null; + + if (props.type == "LFO"){ + typeOption = ListItem(DropDown({onChange: props.setShape, value:props.shape, options: SHAPETYPES})); + } + else if (props.type == "Noise"){ + typeOption = ListItem(DropDown({onChange: props.setNoise, value:props.noise, options: NOISETYPES})); + } + let content = e('ul', {className: 'lfo-item'}, ListItem(DropDown({onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), - ListItem(ControlType()), - ListItem(DropDown({onChange: props.setShape, value:props.shape, options: SHAPETYPES})), + ListItem(DropDown({options: TYPEOPTIONS, onChange: props.setType, value:props.type})), + typeOption, ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), ListItem(e("input", {onChange:props.setFreq, value:props.freq, className:"timeInput"}, null)), ListItem(e(NumberBox, {onChange:props.setMin, value:props.min, step:0.1}, null)), From a608b083f322fc50a9e2a91227ab91bf78ea7007 Mon Sep 17 00:00:00 2001 From: Kieran McAuliffe Date: Wed, 25 Sep 2024 15:59:27 +0200 Subject: [PATCH 3/7] working sine interpolation --- lfogui.js | 18 ++++++++++------- modulators.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/lfogui.js b/lfogui.js index 7dc65bc..9b34d80 100644 --- a/lfogui.js +++ b/lfogui.js @@ -66,10 +66,14 @@ function MasterLfoHandler(){ const [minArr, setMinArr] = React.useState(Array(MAXLFOS).fill('0')); const [maxArr, setMaxArr] = React.useState(Array(MAXLFOS).fill('1')); - const [phaseArr, setPhaseArr] = React.useState(Array(MAXLFOS).fill('0')); + const [initPhaseArr, setInitPhaseArr] = React.useState(Array(MAXLFOS).fill('0')); + const [lastPhaseArr, setLastPhaseArr] = React.useState(Array(MAXLFOS).fill(0)); + const [cachedNoiseValueArr, setCachedNoiseValueArr] = React.useState(Array(MAXLFOS).fill([0, 0])); - const allModArrays = [modVisibleArr, modTypeArr, modInstanceNumArr, shapeArr, noiseTypeArr, djParamArr, freqArr, minArr, maxArr, phaseArr]; - const allModSetters = [setModVisibleArr, setModTypeArr, setModInstanceNumArr, setShapeArr, setNoiseTypeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setPhaseArr]; + + + const allModArrays = [modVisibleArr, modTypeArr, modInstanceNumArr, shapeArr, noiseTypeArr, djParamArr, freqArr, minArr, maxArr, initPhaseArr]; + const allModSetters = [setModVisibleArr, setModTypeArr, setModInstanceNumArr, setShapeArr, setNoiseTypeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setInitPhaseArr]; const modBlankVals = [true, '1', SHAPETYPES[0], MODPARAMOPTIONS[0], '1hz', '0', '1', '0']; @@ -202,8 +206,8 @@ function MasterLfoHandler(){ function handleTick(event) { let time = (Date.now() - firstUpdateTime) / 1000; - - operateModulators(modVisibleArr, modInstanceNumArr, djParamArr, modCenterVals, freqArr, minArr, maxArr, shapeArr, phaseArr, userDefinedWave, time, beatsInMeasure, ticks); + let noiseData = {lastPhaseArr, setLastPhaseArr, cachedNoiseValueArr, setCachedNoiseValueArr}; + operateModulators(modVisibleArr, modTypeArr, modInstanceNumArr, djParamArr, modCenterVals, freqArr, minArr, maxArr, shapeArr, initPhaseArr, noiseData, userDefinedWave, time, beatsInMeasure, ticks); } function handleTimeSig(event) { @@ -284,8 +288,8 @@ function MasterLfoHandler(){ max: maxArr[i], setMax: CreateParamChanger(maxArr, setMaxArr, i), - phase: phaseArr[i], - setPhase: CreateParamChanger(phaseArr, setPhaseArr, i), + phase: initPhaseArr[i], + setPhase: CreateParamChanger(initPhaseArr, setInitPhaseArr, i), visible: modVisibleArr[i], linked: CheckLinked(modInstanceNumArr[i], djParamArr[i], enumInstanceNumArr, enumDjParamArr), diff --git a/modulators.js b/modulators.js index 0f33b22..1190080 100644 --- a/modulators.js +++ b/modulators.js @@ -78,7 +78,7 @@ function indexWave(type, phase, userDefinedWave){ } } -function operateModulators(visibleArr, instanceNumArr, paramNames, centers, freqs, mins, maxs, waveTypes, phaseArr, userDefinedWave, currTime, beatsInMeasure, ticks){ +function operateModulators(visibleArr, typeArr, instanceNumArr, paramNames, centers, freqs, mins, maxs, waveTypes, phaseArr, noiseData, userDefinedWave, currTime, beatsInMeasure, ticks){ for (let i=0; i phase){ // occurs if the phase reset to 0 or at the very start + + noiseData.cachedNoiseValueArr[index][0] = noiseData.cachedNoiseValueArr[index][1]; + if (noiseData.cachedNoiseValueArr[index][0] == 0) + noiseData.cachedNoiseValueArr[index][0] = center; + + noiseData.cachedNoiseValueArr[index][1] = Math.random(); + noiseData.setCachedNoiseValueArr(noiseData.cachedNoiseValueArr); + } + noiseData.lastPhaseArr[index] = phase; + noiseData.setLastPhaseArr(noiseData.lastPhaseArr); + + let sinePhase = (Math.sin(Math.PI + Math.PI * phase) + 1) / 2 + + let unscaled = (noiseData.cachedNoiseValueArr[index][1] - noiseData.cachedNoiseValueArr[index][0]) * sinePhase + noiseData.cachedNoiseValueArr[index][0]; + syncDisplay(inst, name, unscaled); return unscaled * amp + center + parseFloat(min); + } // actual returns the period for musical timing, to avoid floating point errors From 43e517cc4ef136765dc1651309ce61b81c91704c Mon Sep 17 00:00:00 2001 From: trian-gles <69212477+trian-gles@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:40:47 +0200 Subject: [PATCH 4/7] added new init values --- lfogui.js | 10 ++++++---- modulators.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lfogui.js b/lfogui.js index 9b34d80..d18133e 100644 --- a/lfogui.js +++ b/lfogui.js @@ -72,9 +72,9 @@ function MasterLfoHandler(){ - const allModArrays = [modVisibleArr, modTypeArr, modInstanceNumArr, shapeArr, noiseTypeArr, djParamArr, freqArr, minArr, maxArr, initPhaseArr]; - const allModSetters = [setModVisibleArr, setModTypeArr, setModInstanceNumArr, setShapeArr, setNoiseTypeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setInitPhaseArr]; - const modBlankVals = [true, '1', SHAPETYPES[0], MODPARAMOPTIONS[0], '1hz', '0', '1', '0']; + const allModArrays = [modVisibleArr, modTypeArr, modInstanceNumArr, shapeArr, noiseTypeArr, djParamArr, freqArr, minArr, maxArr, initPhaseArr, lastPhaseArr, cachedNoiseValueArr]; + const allModSetters = [setModVisibleArr, setModTypeArr, setModInstanceNumArr, setShapeArr, setNoiseTypeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setInitPhaseArr, lastPhaseArr, cachedNoiseValueArr]; + const modBlankVals = [true, 'LFO', '1', SHAPETYPES[0], MODPARAMOPTIONS[0], "Sine Int.", '1hz', '0', '1', '0', 0, [0, 0]]; /// ENUMERATOR ARRAYS @@ -118,7 +118,6 @@ function MasterLfoHandler(){ React.useEffect(() => { function handleLoad(event) { - window.max.getDict(event.detail, (dict) => { for (let i = 0; i { + if (id < MAXLFOS - 1){ + if (modVisibleArr[id + 1]){ let emptyIndex = modVisibleArr.findIndex((item) => !item); @@ -310,6 +311,7 @@ function MasterLfoHandler(){ } } else { + log("adding lfo"); for (var j = 0; j < allModArrays.length; j++){ // no space below, easy. let array = allModArrays[j]; array[id + 1] = modBlankVals[j]; diff --git a/modulators.js b/modulators.js index 1190080..80445aa 100644 --- a/modulators.js +++ b/modulators.js @@ -90,6 +90,7 @@ function operateModulators(visibleArr, typeArr, instanceNumArr, paramNames, cent } let output = 0; + if (typeArr[i] == "LFO") output = operateLFO(center, inst, freqs[i], mins[i], maxs[i], waveTypes[i], phaseArr, i, userDefinedWave, name, currTime, beatsInMeasure, ticks); else @@ -101,7 +102,6 @@ function operateModulators(visibleArr, typeArr, instanceNumArr, paramNames, cent } function operateLFO(center, inst, timeBaseStr, min, max, waveType, phaseArr, phaseIndex, userDefinedWave, name, currTime, beatsInMeasure, maxTicks){ - let amp = parseFloat(max) - parseFloat(min); let phaseType; let timeBase; From d22cdc840122ef4bded023dc06aa421e6406728d Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 10 Oct 2024 12:23:50 +0200 Subject: [PATCH 5/7] basic random value noise functions --- lfogui.js | 2 +- modulators.js | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lfogui.js b/lfogui.js index d18133e..1a1d9cb 100644 --- a/lfogui.js +++ b/lfogui.js @@ -205,7 +205,7 @@ function MasterLfoHandler(){ function handleTick(event) { let time = (Date.now() - firstUpdateTime) / 1000; - let noiseData = {lastPhaseArr, setLastPhaseArr, cachedNoiseValueArr, setCachedNoiseValueArr}; + let noiseData = {lastPhaseArr, setLastPhaseArr, cachedNoiseValueArr, setCachedNoiseValueArr, noiseTypeArr}; operateModulators(modVisibleArr, modTypeArr, modInstanceNumArr, djParamArr, modCenterVals, freqArr, minArr, maxArr, shapeArr, initPhaseArr, noiseData, userDefinedWave, time, beatsInMeasure, ticks); } diff --git a/modulators.js b/modulators.js index 80445aa..618fd2d 100644 --- a/modulators.js +++ b/modulators.js @@ -5,7 +5,7 @@ var TYPEOPTIONS = ["LFO", "Noise"]; var SHAPETYPES = ["Sine", "SawUp", "SawDown", "Tri", "Square", "Custom"]; -var NOISETYPES = ["Sine Int."] +var NOISETYPES = ["Rand", "Line Int.", "Sine Int."] var INSTANCEOPTIONS = ["1", "2", "3", "4"]; @@ -132,6 +132,7 @@ function operateNoise(center, inst, timeBaseStr, min, max, waveType, phaseArr, i let amp = parseFloat(max) - parseFloat(min); let phaseType; let timeBase; + let noiseType = noiseData.noiseTypeArr[index]; [timeBase, phaseType] = parseLfoTime(timeBaseStr, beatsInMeasure); let phase; @@ -156,13 +157,31 @@ function operateNoise(center, inst, timeBaseStr, min, max, waveType, phaseArr, i let sinePhase = (Math.sin(Math.PI + Math.PI * phase) + 1) / 2 - let unscaled = (noiseData.cachedNoiseValueArr[index][1] - noiseData.cachedNoiseValueArr[index][0]) * sinePhase + noiseData.cachedNoiseValueArr[index][0]; + //let unscaled = (noiseData.cachedNoiseValueArr[index][1] - noiseData.cachedNoiseValueArr[index][0]) * sinePhase + noiseData.cachedNoiseValueArr[index][0]; + let unscaled = interpolateNoise(noiseData.noiseTypeArr[index], noiseData.cachedNoiseValueArr[index][0], noiseData.cachedNoiseValueArr[index][1], phase); syncDisplay(inst, name, unscaled); return unscaled * amp + center + parseFloat(min); } +function interpolateNoise(type, cachedVal1, cachedVal2, phase){ + let interpVal; + + switch (type){ + case "Sine Int.": + interpVal = (Math.sin(Math.PI + Math.PI * phase) + 1) / 2; + break; + case "Rand": + interpVal = 0; + break; + case "Line Int.": + interpVal = phase; + break; + } + return (cachedVal2 - cachedVal1) * interpVal + cachedVal1; +} + // actual returns the period for musical timing, to avoid floating point errors function parseLfoTime(lfoTime, beatsInMeasure){ if (lfoTime.slice(-2) == "hz"){ From 34e99f09fda7eef2efece18821d88ab0ca311f83 Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 11 Oct 2024 12:52:54 +0200 Subject: [PATCH 6/7] lock mode --- common.js | 12 +- enums.js | 20 +-- lfogui.css | 401 +++++++++++++++++++++++++++++++------------------- lfogui.js | 29 +++- modulators.js | 14 +- 5 files changed, 298 insertions(+), 178 deletions(-) diff --git a/common.js b/common.js index 96cb58e..ba94045 100644 --- a/common.js +++ b/common.js @@ -5,7 +5,8 @@ function isNumeric(str) { } function DropDown(props) { - return e('select', {type: "number", onChange: props.onChange, value: props.value}, + let className = props.locked ? 'locked-component' : ''; + return e('select', {className, type: "number", onChange: props.onChange, value: props.value}, ...props.options.map((item) => Option(item))); } @@ -18,11 +19,13 @@ function Label(text){ } function NumberBox(props){ - return e('input', {type: "number", onChange: props.onChange, step: props.step, value: props.value, className: props.className}, null); + let extraClassName = props.locked ? ' locked-component' : ''; + return e('input', {type: "number", onChange: props.onChange, step: props.step, value: props.value, className: props.className + extraClassName}, null); } function TextBox(props){ - return e('input', {type: "text", value: props.value, onChange: props.onChange, id: props.id}); + let className = props.locked ? 'locked-component' : ''; + return e('input', {className, type: "text", value: props.value, onChange: props.onChange, id: props.id}); } function Option(str, value){ @@ -30,7 +33,8 @@ function Option(str, value){ } function Button(props){ - return e('button', {onClick: props.onClick}, props.text); + let className = props.locked ? 'locked-component' : ''; + return e('button', {onClick: props.onClick, className}, props.text); } function Switch(props){ diff --git a/enums.js b/enums.js index 0c911af..03edde9 100644 --- a/enums.js +++ b/enums.js @@ -5,13 +5,13 @@ // NOT A REACT FUNCTIONAL COMPONENT. MERELY RETURNS AN ARRAY WHICH IS UNPACKED -function EnumeratorItems(index, enumBreakPoints, setEnumBreakPoints, enumNames, setEnumNames, djParam){ +function EnumeratorItems(index, enumBreakPoints, setEnumBreakPoints, enumNames, setEnumNames, djParam, locked){ let items = []; for (let i = 0; i < MAXENUMPOINTS; i++){ - items.push(ListItem(e(TextBox, {onChange: CreateMatrixParamChanger(enumNames, setEnumNames, index, i), value: enumNames[index][i], id:`text-${djParam}-${enumNames[index][i]}`}, null))); + items.push(ListItem(e(TextBox, {locked, onChange: CreateMatrixParamChanger(enumNames, setEnumNames, index, i), value: enumNames[index][i], id:`text-${djParam}-${enumNames[index][i]}`}, null))); // Add 1 to make up for the lower enum bound - items.push(ListItem(e(NumberBox, {onChange: CreateMatrixParamChanger(enumBreakPoints, setEnumBreakPoints, index, i + 1), value:enumBreakPoints[index][i + 1]}, null))); + items.push(ListItem(e(NumberBox, {locked, onChange: CreateMatrixParamChanger(enumBreakPoints, setEnumBreakPoints, index, i + 1), value:enumBreakPoints[index][i + 1]}, null))); } return items; } @@ -20,13 +20,13 @@ function EnumeratorRow(props){ let linkedText = props.linked ? "<- mods" : ""; let content = e('ul', {className: 'lfo-item', id: `${props.djParam}-enum-row`}, - ListItem(DropDown({onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), - ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), - ListItem(e(NumberBox, {onChange: props.setEnumItemCounts, step:1, value:props.enumItems, className: 'enum-count'}, null)), - ListItem(e(NumberBox, {onChange: CreateMatrixParamChanger(props.enumBreakPoints, props.setEnumBreakPoints, props.index, 0), value:props.enumBreakPoints[props.index][0], step:0.1}, null)), - ...(EnumeratorItems(props.index, props.enumBreakPoints, props.setEnumBreakPoints, props.enumNames, props.setEnumNames, props.djParam).slice(0, props.enumItems * 2)), - ListItem(e(Button, {text:'+', onClick: props.addEnum}, null)), - ListItem(e(Button, {text:'-', onClick: props.removeEnum}, null)), + ListItem(DropDown({locked:props.locked, onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), + ListItem(DropDown({locked:props.locked, onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), + ListItem(e(NumberBox, {locked:props.locked, onChange: props.setEnumItemCounts, step:1, value:props.enumItems, className: 'enum-count'}, null)), + ListItem(e(NumberBox, {locked:props.locked, onChange: CreateMatrixParamChanger(props.enumBreakPoints, props.setEnumBreakPoints, props.index, 0), value:props.enumBreakPoints[props.index][0], step:0.1}, null)), + ...(EnumeratorItems(props.index, props.enumBreakPoints, props.setEnumBreakPoints, props.enumNames, props.setEnumNames, props.djParam, props.locked).slice(0, props.enumItems * 2)), + ListItem(e(Button, {locked:props.locked, text:'+', onClick: props.addEnum}, null)), + ListItem(e(Button, {locked:props.locked, text:'-', onClick: props.removeEnum}, null)), ListItem(e("div", {className:"linked"}, linkedText)) ); if (props.visible){ diff --git a/lfogui.css b/lfogui.css index 5860643..17f5ca3 100644 --- a/lfogui.css +++ b/lfogui.css @@ -1,181 +1,272 @@ -html, - body { - width: 100%; - height: 100%; - margin: 0px; - border: 0; - overflow: hidden; /* Disable scrollbars */ - display: block; /* No floating content on sides */ - } +* { + --locked-color: #5fadbf; + --unlocked-color: #ff5153; +} - ul { - list-style-type: none; - margin: 0; - padding: 0; - overflow: hidden; - background-color: #333333; - } +html, body { + width: 100%; + height: 100%; + margin: 0px; + border: 0; + overflow: hidden; /* Disable scrollbars */ + display: block; /* No floating content on sides */ +} - li { - float: left; - } +ul { + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; + background-color: #333333; +} - - input[type=number] { - width: 50px; - margin: 0; - padding: 0; - } +li { + float: left; +} - input[type=text] { - width: 60px; - margin: 0; - padding: 0; - font-weight: bold; - } - .timeInput { - width: 80px; - margin: 0; - padding: 0; - } +input[type=number] { + width: 50px; + margin: 0; + padding: 0; +} - #matrix { - background-color: aquamarine; - height: 100%; - width: 100%; - } +input[type=text] { + width: 60px; + margin: 0; + padding: 0; + font-weight: bold; +} - .numbox-unclicked { - user-select: none; - border: solid; - font-size: 12vw; - } +.timeInput { + width: 80px; + margin: 0; + padding: 0; +} - .numbox-clicked { - user-select: none; - border : solid; - font-size: 12vw; - } +#matrix { + background-color: aquamarine; + height: 100%; + width: 100%; +} - .param-input-label { - width: 93%; - font-size: 5vw; - } +.numbox-unclicked { + user-select: none; + border: solid; + font-size: 12vw; +} - .lfo-input-label { - width: 40%; - font-size: 5vw; - } +.numbox-clicked { + user-select: none; + border : solid; + font-size: 12vw; +} - /* The switch - the box around the slider */ - .switch { - position: relative; - display: inline-block; - width: 30px; - height: 17px; - } +.param-input-label { + width: 93%; + font-size: 5vw; +} - /* Hide default HTML checkbox */ - .switch input { - opacity: 0; - width: 0; - height: 0; - } +.lfo-input-label { + width: 40%; + font-size: 5vw; +} - /* The slider */ - .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #ccc; - -webkit-transition: .4s; - transition: .4s; - } + /* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 30px; + height: 17px; +} - .slider:before { - position: absolute; - content: ""; - height: 13px; - width: 13px; - left: 2px; - bottom: 2px; - background-color: white; - -webkit-transition: .4s; - transition: .4s; - } +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} - input:checked + .slider { - background-color: #2196F3; - } +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} - input:focus + .slider { - box-shadow: 0 0 1px #2196F3; - } +.slider:before { + position: absolute; + content: ""; + height: 13px; + width: 13px; + left: 2px; + bottom: 2px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} - input:checked + .slider:before { - -webkit-transform: translateX(13px); - -ms-transform: translateX(13px); - transform: translateX(13px); - } +input:checked + .slider { + background-color: #2196F3; +} - /* Rounded sliders */ - .slider.round { - border-radius: 17px; - } +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} - .slider.round:before { - border-radius: 50%; - } +input:checked + .slider:before { + -webkit-transform: translateX(13px); + -ms-transform: translateX(13px); + transform: translateX(13px); +} - h5 { - margin: 0; - padding: 0; - } +/* Rounded sliders */ +.slider.round { + border-radius: 17px; +} - .enum-count { - background-color: aquamarine; - } +.slider.round:before { + border-radius: 50%; +} - .label { - background-color: aliceblue; - padding: 0 4px 0 4px; - margin: 0 2px 0 2px; - border-color: #333333; - border-width: 1px; - } +h5 { + margin: 0; + padding: 0; +} - .base-val { - background-color: lightgray; - border-color: #333333; - border-width: 1px; - width: 50px; - margin-left: 2px; - margin-top: 1px; - } +.enum-count { + background-color: aquamarine; +} - .linked { - color: red; - border-width: 1px; - width: 50px; - font-size: small; - margin-left: 2px; - margin-top: 5px; - } - +.label { + background-color: aliceblue; + padding: 0 4px 0 4px; + margin: 0 2px 0 2px; + border-color: #333333; + border-width: 1px; +} - @keyframes pulse-animation { - 0% { - color: black; - } - 100% { - color: red; - } - } +.base-val { + background-color: lightgray; + border-color: #333333; + border-width: 1px; + width: 50px; + margin-left: 2px; + margin-top: 1px; +} - #pulse { - animation: pulse-animation 0.2s normal; - } \ No newline at end of file +.linked { + color: red; + border-width: 1px; + width: 50px; + font-size: small; + margin-left: 2px; + margin-top: 5px; +} + + +@keyframes pulse-animation { + 0% { + color: black; + } + 100% { + color: red; + } +} + +#pulse { + animation: pulse-animation 0.2s normal; +} + +/* :::::::::::::: LOCK CSS */ + +.locked-component { + pointer-events: none; + background-color: #333333; + color : white; +} + +.container { + display: flex; + width: 100%; + justify-content: space-between; +} + +/* Locked */ +.lock { + margin-top: 14px; + width: 24px; + height: 21px; + border: 3px solid var(--locked-color); + border-radius: 5px; + position: relative; + cursor: pointer; + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +.lock:after { + content: ""; + display: block; + background: var(--locked-color); + width: 3px; + height: 7px; + position: absolute; + top: 50%; + left: 50%; + margin: -3.5px 0 0 -2px; + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +.lock:before { + content: ""; + display: block; + width: 10px; + height: 10px; + bottom: 100%; + position: absolute; + left: 50%; + margin-left: -8px; + border: 3px solid var(--locked-color); + border-top-right-radius: 50%; + border-top-left-radius: 50%; + border-bottom: 0; + -webkit-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +/* Locked Hover */ +.lock:hover:before { + height: 12px; +} +/* Unlocked */ +.unlocked { + transform: rotate(10deg); +} +.unlocked:before { + bottom: 130%; + left: 31%; + margin-left: -11.5px; + transform: rotate(-45deg); +} +.unlocked, +.unlocked:before { + border-color: var(--unlocked-color); +} +.unlocked:after { + background: var(--unlocked-color); +} +/* Unlocked Hover */ +.unlocked:hover { + transform: rotate(3deg); +} +.unlocked:hover:before { + height: 10px; + left: 40%; + bottom: 124%; + transform: rotate(-30deg); +} \ No newline at end of file diff --git a/lfogui.js b/lfogui.js index 1a1d9cb..96b1317 100644 --- a/lfogui.js +++ b/lfogui.js @@ -20,6 +20,11 @@ const ViewModes = Object.freeze({ ENUM: 1 }); +const LockModes = Object.freeze({ + UNLOCK: 0, + LOCK: 1 +}); + var modPhases = Array(MAXLFOS).fill(0); var firstUpdateTime = Date.now(); @@ -43,6 +48,14 @@ function MasterLfoHandler(){ setViewMode(ViewModes.MOD); }; + const [lockMode, setLockMode] = React.useState(LockModes.LOCK); + const toggleLockMode = () => { + if (lockMode === LockModes.UNLOCK) + setLockMode(LockModes.LOCK); + else + setLockMode(LockModes.UNLOCK); + }; + /// MODULATOR ARRAYS const [userDefinedWave, setUserDefinedWave] = React.useState(Array(50).fill(0)); @@ -262,6 +275,7 @@ function MasterLfoHandler(){ modContents.push( e(LfoRow, { + locked : lockMode, instanceNum : modInstanceNumArr[i], setInstanceNum: CreateParamChanger(modInstanceNumArr, setModInstanceNumArr, i), @@ -343,7 +357,7 @@ function MasterLfoHandler(){ enumContents.push( e(EnumeratorRow, { index: i, - + locked : lockMode, instanceNum : enumInstanceNumArr[i], setInstanceNum: CreateParamChanger(enumInstanceNumArr, setEnumInstanceNumArr, i), enumItems: enumItemCounts[i], @@ -425,8 +439,19 @@ function MasterLfoHandler(){ labels = ENUMERATORLABELS; } + let lockClass; + if (lockMode == LockModes.LOCK){ + lockClass = 'lock'; + } + else { + lockClass = 'lock unlocked'; + } + return e('div', null, - e(Switch, {ontoggle: toggleViewMode}, null), + e('div', {className: 'container'}, + e(Switch, {ontoggle: toggleViewMode}, null), + e('span', {className: lockClass, onClick: toggleLockMode}, null)), + e('h5', null, title), e('ul', null, ...labels.map(x => ListItem(Label(x)))), e('div', {id: 'grid'}, ...grid) diff --git a/modulators.js b/modulators.js index 618fd2d..c036c9a 100644 --- a/modulators.js +++ b/modulators.js @@ -34,17 +34,17 @@ function LfoRow(props){ let typeOption = null; if (props.type == "LFO"){ - typeOption = ListItem(DropDown({onChange: props.setShape, value:props.shape, options: SHAPETYPES})); + typeOption = ListItem(DropDown({locked:props.locked, onChange: props.setShape, value:props.shape, options: SHAPETYPES})); } else if (props.type == "Noise"){ - typeOption = ListItem(DropDown({onChange: props.setNoise, value:props.noise, options: NOISETYPES})); + typeOption = ListItem(DropDown({locked:props.locked, onChange: props.setNoise, value:props.noise, options: NOISETYPES})); } let content = e('ul', {className: 'lfo-item'}, - ListItem(DropDown({onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), - ListItem(DropDown({options: TYPEOPTIONS, onChange: props.setType, value:props.type})), + ListItem(DropDown({locked:props.locked, onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), + ListItem(DropDown({locked:props.locked, options: TYPEOPTIONS, onChange: props.setType, value:props.type})), typeOption, - ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), + ListItem(DropDown({locked:props.locked, onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), ListItem(e("input", {onChange:props.setFreq, value:props.freq, className:"timeInput"}, null)), ListItem(e(NumberBox, {onChange:props.setMin, value:props.min, step:0.1}, null)), ListItem(e(NumberBox, {onChange:props.setMax, value:props.max, step:0.1}, null)), @@ -52,8 +52,8 @@ function LfoRow(props){ ListItem(e(NumberBox, {onChange:props.setPhase, value:props.phase, step:0.1}, null)), ListItem(e("div", {className:"base-val"}, center.toString())), ListItem(e("input", {type: 'range', min: 0, max: 1, step: 0.01, readonly: true, id: `slider-${props.instanceNum}-${props.djParam}`})), - ListItem(e(Button, {text:'+', onClick: props.addLfo}, null)), - ListItem(e(Button, {text:'-', onClick: props.removeLfo}, null)), + ListItem(e(Button, {text:'+', onClick: props.addLfo, locked: props.locked}, null)), + ListItem(e(Button, {text:'-', onClick: props.removeLfo, locked: props.locked}, null)), ListItem(e("div", {className:"linked"}, linkedText)), ); if (props.visible){ From 64f1455f6986c3ba6d3d4f9bbe4c6d6fefcbe682 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 14 Oct 2024 09:16:05 +0200 Subject: [PATCH 7/7] fixed userwave update --- lfogui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lfogui.js b/lfogui.js index 96b1317..89fcde5 100644 --- a/lfogui.js +++ b/lfogui.js @@ -254,7 +254,7 @@ function MasterLfoHandler(){ window.removeEventListener('userWave', handleChangeUserWave); window.removeEventListener('maxTicks', handleMaxTicks); }; - }, [...allModArrays, ...allEnumArrays, ...allEnumMats, modCenterVals, render, beatsInMeasure, ticks]); + }, [...allModArrays, ...allEnumArrays, ...allEnumMats, userDefinedWave, modCenterVals, render, beatsInMeasure, ticks]); function CheckLinked(inst, param, checkInstArr, checkParamArr){