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..1a1d9cb 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')); @@ -64,11 +66,15 @@ 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, modInstanceNumArr, shapeArr, djParamArr, freqArr, minArr, maxArr, phaseArr]; - const allModSetters = [setModVisibleArr, setModInstanceNumArr, setShapeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setPhaseArr]; - 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 @@ -112,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); @@ -298,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 dc4a778..618fd2d 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 = ["Rand", "Line Int.", "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)), @@ -66,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]; + 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