///////////////////////// // MODULATORS ///////////////////////// var TYPEOPTIONS = ["LFO", "Noise"]; var SHAPETYPES = ["Sine", "SawUp", "SawDown", "Tri", "Square", "Custom_1", "Custom_2", "Custom_3", "Custom_4"]; var NOISETYPES = ["Rand", "Line Int.", "Sine Int."] var INSTANCEOPTIONS = ["1", "2", "3", "4"]; const MODPARAMOPTIONS = ["NONE", "stream", "pulse_length", "eventfulness", "event_length", "metriclarity", "harmoniclarity", "melodic_cohesion", "melody_scope", "tonic_pitch", "pitch_center", "pitch_range", "dynamics", "attenuation", "chordal_weight", "tonality-profile", "ostinato-buffer", "ostinato", "meter", "scale"]; const PhaseTypes = Object.freeze({ MUSICAL: Symbol("musical"), TIME: Symbol("time") }); function ControlType(){ return e('select', {className: 'control-type'}, Option("LFO")); } function DataCell(element) { return e('td', null, element); } function LfoRow(props){ let linkedText = props.linked ? "-> enums" : ""; let center = props.centerVals[props.instanceNum][props.djParam]; if (!center) center = 0; let typeOption = null; if (props.type == "LFO"){ typeOption = DataCell(DropDown({locked:props.locked, onChange: props.setShape, value:props.shape, options: SHAPETYPES})); } else if (props.type == "Noise"){ typeOption = DataCell(DropDown({locked:props.locked, onChange: props.setNoise, value:props.noise, options: NOISETYPES})); } let content = e('tr', {className: 'lfo-item'}, DataCell(DropDown({locked:props.locked, onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})), DataCell(DropDown({locked:props.locked, options: TYPEOPTIONS, onChange: props.setType, value:props.type})), typeOption, DataCell(DropDown({locked:props.locked, onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})), DataCell(e("input", {onChange:props.setFreq, value:props.freq, className:"timeInput"}, null)), DataCell(e(NumberBox, {onChange:props.setMin, value:props.min, step:0.1}, null)), DataCell(e(NumberBox, {onChange:props.setMax, value:props.max, step:0.1}, null)), //DataCell(e(NumberBox, {onChange:props.setAmp, value:props.amp, step:0.1}, null)), DataCell(e(NumberBox, {onChange:props.setPhase, value:props.phase, step:0.1}, null)), DataCell(e("div", {className:"base-val"}, center.toString())), DataCell(e("input", {type: 'range', min: 0, max: 1, step: 0.01, readonly: true, id: `slider-${props.instanceNum}-${props.djParam}`})), DataCell(e(Button, {text:'+', onClick: props.addLfo, locked: props.locked}, null)), DataCell(e(Button, {text:'-', onClick: props.removeLfo, locked: props.locked}, null)), DataCell(e("div", {className:"linked"}, linkedText)), ); if (props.visible){ return content; }; } function indexUserWave(phase, index, userDefinedWaves){ return parseFloat(userDefinedWaves[index][Math.floor(phase * 50)]) / 127; } function indexUserFunction(phase, index, userDefinedFunctions){ return parseFloat(userDefinedFunctions[index][Math.floor(phase * 101)]) / 127; } function indexWave(type, phase, userDefinedWaves, userDefinedFunctions, userDefinedTypes){ switch (type){ case "Sine": return (Math.sin(phase * Math.PI * 2) / 2) + 0.5; case "SawUp": return phase; case "SawDown": return 1 - phase; case "Tri": return phase > 0.5? (1-phase) * 2 : phase * 2; case "Square": return +(phase > 0.5); case "Custom_1": return userDefinedTypes[0] == 0 ? indexUserWave(phase, 1, userDefinedWaves) : indexUserFunction(phase, 1, userDefinedFunctions); case "Custom_2": return userDefinedTypes[1] == 0 ? indexUserWave(phase, 2, userDefinedWaves) : indexUserFunction(phase, 2, userDefinedFunctions); case "Custom_3": return userDefinedTypes[2] == 0 ? indexUserWave(phase, 3, userDefinedWaves) : indexUserFunction(phase, 3, userDefinedFunctions); case "Custom_4": return userDefinedTypes[3] == 0 ? indexUserWave(phase, 4, userDefinedWaves) : indexUserFunction(phase, 4, userDefinedFunctions); } } function operateModulators(visibleArr, typeArr, instanceNumArr, paramNames, centers, freqs, mins, maxs, waveTypes, phaseArr, noiseData, userDefinedWaves, userDefinedFunctions, userDefinedTypes, currTime, beatsInMeasure, ticks){ for (let i=0; i phase){ // occurs if the phase reset to 0 or at the very start noiseData.cachedNoiseValueArr2[index] = noiseData.cachedNoiseValueArr1[index]; if (noiseData.cachedNoiseValueArr1[index] == 0) noiseData.cachedNoiseValueArr2[index] = center; noiseData.cachedNoiseValueArr1[index] = Math.random(); noiseData.setCachedNoiseValueArr1(noiseData.cachedNoiseValueArr1); noiseData.setCachedNoiseValueArr2(noiseData.cachedNoiseValueArr2); } noiseData.lastPhaseArr[index] = phase; noiseData.setLastPhaseArr(noiseData.lastPhaseArr); //let unscaled = (noiseData.cachedNoiseValueArr[index][1] - noiseData.cachedNoiseValueArr[index][0]) * sinePhase + noiseData.cachedNoiseValueArr[index][0]; let unscaled = interpolateNoise(noiseType, noiseData.cachedNoiseValueArr1[index], noiseData.cachedNoiseValueArr2[index], 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"){ return [parseFloat(lfoTime.slice(0, -2)), PhaseTypes.TIME]; } else if (lfoTime.slice(-2) == "ms"){ return [1000 / parseFloat(lfoTime.slice(0, -2)), PhaseTypes.TIME]; } else if (lfoTime.slice(-1) == "s"){ return [1 / parseFloat(lfoTime.slice(0, -1)), PhaseTypes.TIME]; } else if ((lfoTime.match(/:/g) || []).length == 2){ return [1 / moment.duration(lfoTime).asSeconds(), PhaseTypes.TIME]; } else if ((lfoTime.match(/\./g) || []).length == 2){ return [musicalTimingToFreq(...lfoTime.split('.'), beatsInMeasure), PhaseTypes.MUSICAL]; } else { return [0, PhaseTypes.TIME]; } } function musicalTimingToFreq(bars, beats, ticks, beatsInMeasure){ let totalTicks = (parseFloat(bars) * parseFloat(beatsInMeasure) + parseFloat(beats)) * 480 + parseFloat(ticks); return totalTicks; }