// const { createElement } = require("./react"); const DEBUG = false; var log; if (DEBUG) log = console.log; else log = (msg) => {window.max.outlet("debug " + msg)}; const e = React.createElement; let lfos = []; const MAXLFOS = 20; const MAXENUMS = 20; const MAXENUMPOINTS = 10; const ViewModes = Object.freeze({ MOD: 0, ENUM: 1 }); const LockModes = Object.freeze({ UNLOCK: 0, LOCK: 1 }); var modPhases = Array(MAXLFOS).fill(0); var firstUpdateTime = Date.now(); const MODULATORLABELS = ["inst", "-type-", "---shape---", "-------param-------", "--timebase--", "-min-", "-max", "-phase-", "center"]; const ENUMERATORLABELS = ["inst", "---parameter---", "-# points-"]; function parseLfoTimeNonMusical(lfoTime){ if (lfoTime.slice(-2) == "hz"){ return parseFloat(lfoTime.slice(0, -2)); } else if (lfoTime.slice(-2) == "ms"){ return 1000 / parseFloat(lfoTime.slice(0, -2)); } else if (lfoTime.slice(-1) == "s"){ return 1 / parseFloat(lfoTime.slice(0, -1)); } else if ((lfoTime.match(/:/g) || []).length == 2){ return 1 / moment.duration(lfoTime).asSeconds(); } else if ((lfoTime.match(/\./g) || []).length == 2){ return 0; // ignore musical timings } else { return 0; } } function nnFreqToHzString(num){ return `${num}hz`; } function MasterLfoHandler(){ let initVisArr = Array(MAXLFOS).fill(false); initVisArr[0] = true; const [viewMode, setViewMode] = React.useState(ViewModes.MOD); const toggleViewMode = () => { if (viewMode === ViewModes.MOD) setViewMode(ViewModes.ENUM); else 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)); 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':{}}); 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')); const [freqArr, setFreqArr] = React.useState(Array(MAXLFOS).fill('1hz')); // const [ampArr, setAmpArr] = React.useState(Array(MAXLFOS).fill('1')); const [minArr, setMinArr] = React.useState(Array(MAXLFOS).fill('0')); const [maxArr, setMaxArr] = React.useState(Array(MAXLFOS).fill('1')); const [initPhaseArr, setInitPhaseArr] = React.useState(Array(MAXLFOS).fill('0')); const [lastPhaseArr, setLastPhaseArr] = React.useState(Array(MAXLFOS).fill(0)); const [cachedNoiseValueArr1, setCachedNoiseValueArr1] = React.useState(Array(MAXLFOS).fill(0)); const [cachedNoiseValueArr2, setCachedNoiseValueArr2] = React.useState(Array(MAXLFOS).fill(0)); const allModArrays = [modVisibleArr, modTypeArr, modInstanceNumArr, shapeArr, noiseTypeArr, djParamArr, freqArr, minArr, maxArr, initPhaseArr, lastPhaseArr, cachedNoiseValueArr1, cachedNoiseValueArr2]; const allModSetters = [setModVisibleArr, setModTypeArr, setModInstanceNumArr, setShapeArr, setNoiseTypeArr, setDjParamArr, setFreqArr, setMinArr, setMaxArr, setInitPhaseArr, setLastPhaseArr, setCachedNoiseValueArr1, setCachedNoiseValueArr2]; const modBlankVals = [true, 'LFO', '1', SHAPETYPES[0], NOISETYPES[0], MODPARAMOPTIONS[0], '1hz', '0', '1', '0', 0, 0, 0]; /// ENUMERATOR ARRAYS const [enumVisibleArr, setEnumVisibleArr] = React.useState(initVisArr); const [enumInstanceNumArr, setEnumInstanceNumArr] = React.useState(Array(MAXLFOS).fill('1')); const [enumItemCounts, setEnumItemCounts] = React.useState(Array(MAXENUMPOINTS).fill('2')); const [enumDjParamArr, setEnumDjParamArr] = React.useState(Array(MAXENUMPOINTS).fill('attenuation')); let baseEnumBreakpoints = Array(MAXENUMS).fill(0).map(x => Array(MAXENUMPOINTS+ 1).fill(0)); for (let i = 0; i < MAXENUMS; i++){ for (let j=0; j < MAXENUMPOINTS + 1; j++){ baseEnumBreakpoints[i][j] = j; } } const [enumBreakPoints, setEnumBreakPoints] = React.useState(baseEnumBreakpoints); const getBlankEnumBreakPointRow = () => { let arr = [] for (let i=0; i< MAXENUMPOINTS + 1; i++) arr.push(i) return arr; } const getBlankEnumNameRow = () => {return Array(MAXENUMPOINTS).fill('param')}; let baseEnumNames = Array(MAXENUMS).fill(0).map(x => Array(MAXENUMPOINTS).fill('param')); const [enumNames, setEnumNames] = React.useState(baseEnumNames); const allEnumArrays = [enumVisibleArr, enumInstanceNumArr, enumItemCounts, enumDjParamArr]; const allEnumArrSetters = [setEnumVisibleArr, setEnumInstanceNumArr, setEnumItemCounts, setEnumDjParamArr]; const allEnumMats = [enumBreakPoints, enumNames]; const allEnumMatSetters = [setEnumBreakPoints, setEnumNames]; const allGetEnumMatBlankVals = [getBlankEnumBreakPointRow, getBlankEnumNameRow] const enumBlankVals = [true, '1', 2, MODPARAMOPTIONS[0]]; const [render, rerender] = React.useState(false); // BAD. I SHOULDN'T BE DOING THIS React.useEffect(() => { function handleLoad(event) { window.max.getDict(event.detail, (dict) => { for (let i = 0; i { allNNData.push(parseLfoTimeNonMusical(element)); }); allNNData = allNNData.concat(minArr); allNNData = allNNData.concat(maxArr); window.max.outlet("NNdata " + allNNData) } window.addEventListener('loadDict', handleLoad); window.addEventListener('saveDict', handleSave); window.addEventListener('dumpNN', dumpNN); window.addEventListener('setNN', setNN); window.addEventListener('tick', handleTick); window.addEventListener('param', handleParam); window.addEventListener('enum', handleEnum); window.addEventListener('timesig', handleTimeSig); window.addEventListener('userWave', handleChangeUserWave); window.addEventListener('maxTicks', handleMaxTicks); return () => { window.removeEventListener('loadDict', handleLoad); window.removeEventListener('saveDict', handleSave); window.removeEventListener('dumpNN', dumpNN); window.removeEventListener('setNN', setNN); window.removeEventListener('tick', handleTick); window.removeEventListener('param', handleParam); window.removeEventListener('enum', handleEnum); window.removeEventListener('timesig', handleTimeSig); window.removeEventListener('userWave', handleChangeUserWave); window.removeEventListener('maxTicks', handleMaxTicks); }; }, [...allModArrays, ...allEnumArrays, ...allEnumMats, userDefinedWave, modCenterVals, render, beatsInMeasure, ticks]); function CheckLinked(inst, param, checkInstArr, checkParamArr){ for (let i = 0; i < checkInstArr.length; i++){ if (checkInstArr[i] == inst && checkParamArr[i] == param) return true; } return false; } /////// // Generate Modulators /////// let modContents = [] for (var i = 0; i { if (id < MAXLFOS - 1){ if (modVisibleArr[id + 1]){ let emptyIndex = modVisibleArr.findIndex((item) => !item); if (emptyIndex != -1){ for (var j = 0; j < allModArrays.length; j++){ let array = allModArrays[j]; // remove from all arrays array.splice(emptyIndex, 1); // add empty item at opened index array.splice(id + 1, 0, modBlankVals[j]); allModSetters[j](array); } } } else { for (var j = 0; j < allModArrays.length; j++){ // no space below, easy. let array = allModArrays[j]; array[id + 1] = modBlankVals[j]; log(j); log(allModArrays.length); allModSetters[j](array); } } rerender(!render); } }, removeLfo: () => { if (modVisibleArr.filter(x=>x).length > 1){ let newArr = modVisibleArr.slice(); newArr[id] = false; setModVisibleArr(newArr); } } }, null)); } /////// // Generate Enumerators /////// let enumContents = [] for (var i = 0; i { if (id < MAXLFOS - 1){ if (enumVisibleArr[id + 1]){ // if we need to open up space let emptyIndex = enumVisibleArr.findIndex((item) => !item); if (emptyIndex != -1){ for (var j = 0; j < allEnumArrays.length; j++){ let array = allEnumArrays[j]; // remove from all arrays array.splice(emptyIndex, 1); // add empty item at opened index array.splice(id + 1, 0, enumBlankVals[j]); allEnumArrSetters[j](array); } // Now do the same with matrices for (var j = 0; j < allEnumMats.length; j++){ let mat = allEnumMats[j]; mat.splice(emptyIndex, 1); mat.splice(id + 1, 0, 0); mat[id + 1] = allGetEnumMatBlankVals[j](); allEnumMatSetters[j](mat); } } } else { for (var j = 0; j < allEnumArrays.length; j++){ let array = allEnumArrays[j]; array[id+1] = enumBlankVals[j]; allEnumArrSetters[j](array); } // Now do the same with matrices for (var j = 0; j < allEnumMats.length; j++){ let mat = allEnumMats[j]; mat[id + 1] = allGetEnumMatBlankVals[j](); allEnumMatSetters[j](mat); } } rerender(!render); } }, removeEnum: () => { if (enumVisibleArr.filter(x=>x).length > 1){ let newArr = enumVisibleArr.slice(); newArr[id] = false; setEnumVisibleArr(newArr); } } }, null) ); } var grid; var title; var labels; if (viewMode === ViewModes.MOD){ grid = modContents; title = "MODULATORS"; labels = MODULATORLABELS; } else { grid = enumContents; title = "ENUMERATORS"; labels = ENUMERATORLABELS; } let lockClass; if (lockMode == LockModes.LOCK){ lockClass = 'lock'; } else { lockClass = 'lock unlocked'; } return e('div', 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) ); } if (!DEBUG){ window.max.bindInlet("load", (dictId) => { window.dispatchEvent(new CustomEvent('loadDict', {'detail' : dictId})); }); window.max.bindInlet("save", (dictId) => { window.dispatchEvent(new CustomEvent('saveDict', {'detail' : dictId})); }); window.max.bindInlet("dumpNN", () => { window.dispatchEvent(new CustomEvent('dumpNN')); }); window.max.bindInlet("setNN", (...data) => { window.dispatchEvent(new CustomEvent('setNN', {'detail' : data})); }); window.max.bindInlet("param", (inst, paramName, val) => { window.dispatchEvent(new CustomEvent('param', {'detail' : [inst, paramName, val]})); }); window.max.bindInlet("timesig", (top, bottom) => { window.dispatchEvent(new CustomEvent('timesig', {'detail' : [top, bottom]})); }); window.max.bindInlet("ticks", (val) => { window.dispatchEvent(new CustomEvent('maxTicks', {'detail' : val})); }); window.max.bindInlet("userWave", (...points) => { window.dispatchEvent(new CustomEvent('userWave', {'detail' : points})); }); /* window.max.binInlet("userWave", (...points) => { window.dispatchEvent(new CustomEvent('userWave', {'detail' : [points]})); log("received user points"); }); */ setInterval(() => { window.dispatchEvent(new CustomEvent('tick')); }, 200); } const root = ReactDOM.createRoot(document.getElementById('lfo-container')); root.render(e(MasterLfoHandler, null, null));