44 Commits

Author SHA1 Message Date
b850440287 improved text placement 2025-07-29 14:10:14 +02:00
7ac026f35f initial help patch 2025-07-22 15:27:16 +02:00
7cb4180d97 Merge pull request 'setting viewMode directly from Max' (#16) from viewMode-max into main
Reviewed-on: #16
2025-07-21 16:15:52 +02:00
51767ff058 setting viewMode directly from Max 2025-07-21 16:14:12 +02:00
Eveline-97
fe68764e7d fix aligning 2025-07-02 14:29:59 +02:00
Eveline-97
40d0927a0e streamline layout patch 2025-07-02 12:16:30 +02:00
Eveline-97
92b7545e90 match Max layout to jweb 2025-07-02 12:07:53 +02:00
Eveline-97
4912b398dd Merge remote-tracking branch 'refs/remotes/origin/main'
merge
2025-07-01 16:34:50 +02:00
Eveline-97
02d5c6e914 cleanup modulator table 2025-07-01 16:34:27 +02:00
13f3a18c4c Merge pull request 'bpatcher-embed' (#15) from bpatcher-embed into main
Reviewed-on: #15
2025-07-01 16:32:05 +02:00
f21220e7b2 Merge branch 'main' into bpatcher-embed 2025-07-01 16:31:40 +02:00
fe165b77e5 working saving for bpatcher 2025-07-01 16:30:08 +02:00
Eveline-97
a7b0de9e66 navigation bar 2025-07-01 16:15:41 +02:00
43d37f215f Merge branch 'main' into bpatcher-embed 2025-07-01 16:12:51 +02:00
Eveline-97
c1a0a4d919 basic design 2025-07-01 15:04:49 +02:00
e2f33b8ba7 Merge pull request 'properly saving on adding rows, changing enum length, changing enum params' (#14) from hotfix into main
Reviewed-on: #14
2025-07-01 09:54:22 +02:00
a19717a759 properly saving on adding rows, changing enum length, changing enum params 2025-07-01 09:53:39 +02:00
64da80b742 WIP 2025-07-01 09:25:27 +02:00
Eveline-97
34549daf67 also enumerator items as datacells 2025-07-01 09:20:30 +02:00
Eveline-97
dd47c0b030 enumerators as table, jweb bit wider 2025-07-01 09:18:02 +02:00
0b7dbbb775 Merge branch 'main' into bpatcher-embed 2025-06-30 12:00:29 +02:00
290ff73524 Merge pull request 'more instances of DJster' (#13) from more-dj-instances into main
Reviewed-on: #13
2025-06-30 11:59:31 +02:00
9b1a9f2e03 more instances 2025-06-30 11:58:56 +02:00
541ab6d459 created initial bpatcher file and maxhelp 2025-06-30 11:44:03 +02:00
b96f2aa53b Merge pull request 'button for showing and hiding everything' (#12) from showhide-button into main
Reviewed-on: #12
2025-06-30 11:00:09 +02:00
edcddde844 button for showing and hiding everything 2025-06-30 10:58:51 +02:00
e41567c101 Merge pull request 'buttons for switching between modes' (#11) from button-headers into main
Reviewed-on: #11
2025-06-30 10:22:01 +02:00
8002ae8bdb Merge branch 'main' into button-headers 2025-06-30 10:21:44 +02:00
286a43b0b2 buttons for switching between modes 2025-06-30 10:19:42 +02:00
229ddb9c37 Merge pull request 'increased max number of lfos and enums to 200' (#10) from increase-maxlfos into main
Reviewed-on: #10
2025-06-30 09:46:03 +02:00
bbcfd8faab increased max number of lfos and enums to 200 2025-06-30 09:40:14 +02:00
5d092a607c Merge pull request 'removed lock' (#9) from removing-lock into main
Reviewed-on: #9
2025-06-30 09:32:36 +02:00
de28fd87fa removed lock 2025-06-30 09:31:23 +02:00
Eveline-97
07c4826bd9 table layout for modulators 2025-06-29 17:53:12 +02:00
Eveline-97
501fca079e autopattr save and load waves, functions, types 2025-05-15 10:32:39 +02:00
Eveline-97
3507899eb3 debug function waves works now 2025-05-15 10:18:10 +02:00
Eveline-97
dc2daade1b add function functionality for custom shape 2025-05-14 14:42:34 +02:00
149d1a0b8d save on every UI change 2025-05-13 11:24:39 +02:00
4ee8a93791 four user defined waves 2025-02-12 11:02:51 +01:00
c706461443 Merge pull request 'nn-communication' (#8) from nn-communication into main
Reviewed-on: #8
2024-11-29 14:21:11 +01:00
081e33dd07 modulator noise function previous val bug 2024-11-29 14:20:29 +01:00
eb02657b4a center vals included in NN data 2024-10-22 13:27:07 +02:00
d670aefcbd enums start with NONE 2024-10-22 12:32:42 +02:00
68baa3c8de Merge pull request 'nn-communication' (#7) from nn-communication into main
Reviewed-on: #7
2024-10-22 11:00:25 +02:00
12 changed files with 8604 additions and 3395 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

1540
ModEnum.maxhelp Normal file

File diff suppressed because it is too large Load Diff

3345
ModEnum.maxpat Normal file

File diff suppressed because one or more lines are too long

View File

@@ -43,12 +43,20 @@ function Switch(props){
e('span', {className: 'slider round'}, null))
}
function SendSaveEvent(){
setTimeout(() => {
window.dispatchEvent(new CustomEvent('saveDict', {'detail' : "localStorage"}));
}, 50)
}
function CreateParamChanger(arr, setArr, index, postCB=() => {}, preCB=(val) => val){
return (event) => {
let newArr = arr.slice();
newArr[index] = preCB(event.target.value);
setArr(newArr);
postCB();
SendSaveEvent();
log(`${index} ${event.target.value}`);
}
}
@@ -60,6 +68,7 @@ function CreateMatrixParamChanger(matrix, setMatrix, i, j){
});
newMatrix[i][j] = event.target.value;
setMatrix(newMatrix);
SendSaveEvent();
log(`${i}, ${j} ${event.target.value}`);
}

View File

@@ -4,14 +4,18 @@
function DataCell(element) {
return e('td', null, element);
}
// NOT A REACT FUNCTIONAL COMPONENT. MERELY RETURNS AN ARRAY WHICH IS UNPACKED
function EnumeratorItems(index, enumBreakPoints, setEnumBreakPoints, enumNames, setEnumNames, djParam, locked){
let items = [];
for (let i = 0; i < MAXENUMPOINTS; i++){
items.push(ListItem(e(TextBox, {locked, onChange: CreateMatrixParamChanger(enumNames, setEnumNames, index, i), value: enumNames[index][i], id:`text-${djParam}-${enumNames[index][i]}`}, null)));
items.push(DataCell(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, {locked, onChange: CreateMatrixParamChanger(enumBreakPoints, setEnumBreakPoints, index, i + 1), value:enumBreakPoints[index][i + 1]}, null)));
items.push(DataCell(e(NumberBox, {locked, onChange: CreateMatrixParamChanger(enumBreakPoints, setEnumBreakPoints, index, i + 1), value:enumBreakPoints[index][i + 1]}, null)));
}
return items;
}
@@ -19,15 +23,15 @@ function EnumeratorItems(index, enumBreakPoints, setEnumBreakPoints, enumNames,
function EnumeratorRow(props){
let linkedText = props.linked ? "<- mods" : "";
let content = e('ul', {className: 'lfo-item', id: `${props.djParam}-enum-row`},
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)),
let content = e('tr', {className: 'lfo-item', id: `${props.djParam}-enum-row`},
DataCell(DropDown({locked:props.locked, onChange: props.setInstanceNum, value:props.instanceNum, options: INSTANCEOPTIONS})),
DataCell(DropDown({locked:props.locked, onChange: props.setDjParam, value: props.djParam, options: MODPARAMOPTIONS})),
DataCell(e(NumberBox, {locked:props.locked, onChange: props.setEnumItemCounts, step:1, value:props.enumItems, className: 'enum-count'}, null)),
DataCell(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))
DataCell(e(Button, {locked:props.locked, text:'+', onClick: props.addEnum}, null)),
DataCell(e(Button, {locked:props.locked, text:'-', onClick: props.removeEnum}, null)),
DataCell(e("div", {className:"linked"}, linkedText))
);
if (props.visible){
return content;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -3,45 +3,161 @@
--unlocked-color: #ff5153;
}
html, body {
:root {
--background: white;
--active: royalblue;
--nonactive: rgb(205, 205, 205);
--alert: red;
--textcolor: black;
}
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
html {
background-color: var(--background);
}
body {
font-family: "Poppins", sans-serif;
}
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden; /* Disable scrollbars */
display: block; /* No floating content on sides */
overflow-x: scroll;
overflow-y: scroll;
display: block;
/* No floating content on sides */
}
ul {
list-style-type: none;
/*navigation*/
.header {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
margin: 1em;
border-bottom: 1px solid var(--active);
}
.header button {
border: 1px solid var(--active);
color: var(--active);
background-color: white;
padding: 4px;
text-align: center;
display: inline-block;
padding: 0.5em;
margin: 4px;
font-size: 0.9em;
min-width: 4.5em;
}
td button {
background-color: white;
color: var(--active);
border: 1px solid var(--active);
}
/* :::::::::::::: SELECTING MODULATORS/ENUMERATORS */
.header button.highlighted-button {
color: var(--active);
border: 1px solid var(--active);
}
.header button.unhighlighted-button {
color: var(--nonactive);
border: 1px solid var(--nonactive);
}
/* table */
table {
margin: 1em;
padding: 0em;
border-collapse: collapse;
background-color: lightsteelblue;
}
/* points datacells should have a min-width*/
.enum-count {
min-width: 5.6em;
}
th {
padding: 0.4em 0.3em;
text-align: left;
}
thead {
color: white;
background-color: var(--active);
}
tr,
td {
margin: 0em;
}
td:last-child {
white-space: nowrap;
overflow: hidden;
padding: 0;
}
/* input types */
/* dropdown list */
select {
width: 100%;
}
/*
option, select>* {
font-size: 0.8em !important;
padding: 0em !important;
margin: 0em !important;
min-height: 0em !important;
}
*/
option {
background-color: var(--active);
}
option:not(:checked) {
background-color: white;
}
/* input */
input,
select {
border: 1px solid var(--active);
color: var(--active);
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333333;
line-height: 1.5em;
height: 1.5em;
box-sizing: border-box;
}
li {
float: left;
input {
padding-left: 5px;
/*slight padding on left*/
}
input[type=number] {
width: 50px;
margin: 0;
padding: 0;
}
input[type=text] {
width: 60px;
margin: 0;
padding: 0;
font-weight: bold;
}
.timeInput {
width: 80px;
margin: 0;
padding: 0;
}
#matrix {
@@ -88,51 +204,38 @@ input[type=text] {
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 13px;
width: 13px;
left: 2px;
bottom: 2px;
input[type="range"] {
appearance: none;
-webkit-appearance: none;
height: 8px;
border-radius: 6px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
overflow: hidden;
}
input:checked + .slider {
background-color: #2196F3;
/*slider knob*/
input[type="range"]::-webkit-slider-runnable-track {
-webkit-appearance: none;
color: var(--active);
margin-top: -1px;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(13px);
-ms-transform: translateX(13px);
transform: translateX(13px);
}
/* Rounded sliders */
.slider.round {
border-radius: 17px;
}
.slider.round:before {
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--active);
box-shadow: -80px 0 0 80px var(--active);
}
input[type="range"]::-moz-range-progress {
background-color: var(--active);
}
h5 {
@@ -140,10 +243,6 @@ h5 {
padding: 0;
}
.enum-count {
background-color: aquamarine;
}
.label {
background-color: aliceblue;
padding: 0 4px 0 4px;
@@ -153,12 +252,13 @@ h5 {
}
.base-val {
background-color: lightgray;
border-color: #333333;
border-width: 1px;
border: none;
color: white;
text-align: center;
font-size: 0.9em;
width: 50px;
margin-left: 2px;
margin-top: 1px;
margin: 0;
padding: 0;
}
.linked {
@@ -168,6 +268,8 @@ h5 {
font-size: small;
margin-left: 2px;
margin-top: 5px;
display: none;
/*hide*/
}
@@ -175,6 +277,7 @@ h5 {
0% {
color: black;
}
100% {
color: red;
}
@@ -192,12 +295,6 @@ h5 {
color: white;
}
.container {
display: flex;
width: 100%;
justify-content: space-between;
}
/* Locked */
.lock {
margin-top: 14px;
@@ -210,6 +307,7 @@ h5 {
-webkit-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.lock:after {
content: "";
display: block;
@@ -223,6 +321,7 @@ h5 {
-webkit-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.lock:before {
content: "";
display: block;
@@ -239,31 +338,38 @@ h5 {
-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%;

View File

@@ -7,6 +7,7 @@
-->
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="./lfogui.css">
</head>
@@ -19,10 +20,7 @@
<script src="./common.js"></script>
<script src="./enums.js"></script>
<script src="./modulators.js"></script>
<script src="./lfogui.js">
</script>
<script src="./lfogui.js"></script>
</body>

163
lfogui.js
View File

@@ -9,9 +9,10 @@ else
const e = React.createElement;
let lfos = [];
const MAXLFOS = 20;
const MAXENUMS = 20;
const MAXLFOS = 200;
const MAXENUMS = 200;
const MAXENUMPOINTS = 10;
const MAXUSERDEFINED = 4;
@@ -29,8 +30,8 @@ const LockModes = Object.freeze({
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-"];
const MODULATORLABELS = ["inst", "type", "shape", "param", "timebase", "min", "max", "phase", "center", "result", "", ""];
const ENUMERATORLABELS = ["inst", "parameter", "# points"];
function parseLfoTimeNonMusical(lfoTime) {
@@ -60,19 +61,12 @@ function nnFreqToHzString(num){
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 [lockMode, setLockMode] = React.useState(LockModes.UNLOCK);
const toggleLockMode = () => {
if (lockMode === LockModes.UNLOCK)
setLockMode(LockModes.LOCK);
@@ -80,15 +74,35 @@ function MasterLfoHandler(){
setLockMode(LockModes.UNLOCK);
};
const [enabled, setEnabled] = React.useState(false);
const toggleEnabled = () => {
setEnabled(!enabled);
};
const displayIfEnabled = (content) => {
if (enabled)
return content
}
let toggleEnabledText = enabled ? `Hide \u{25BE}` : `Show \u{25B8}`;
/// MODULATOR ARRAYS
let userDefinedWavesBase = [];
let userDefinedFunctionsBase = [];
let userDefinedTypesBase = [0, 0, 0, 0]; //0 = wave, 1 = function
const [userDefinedWave, setUserDefinedWave] = React.useState(Array(50).fill(0));
for (let i = 0; i < MAXUSERDEFINED; i++) {
userDefinedWavesBase.push(Array(50).fill(0));
userDefinedFunctionsBase.push(Array(101).fill(0));
}
const [userDefinedWaves, setUserDefinedWaves] = React.useState(userDefinedWavesBase);
const [userDefinedFunctions, setUserDefinedFunctions] = React.useState(userDefinedFunctionsBase);
const [userDefinedTypes, setUserDefinedTypes] = React.useState(userDefinedTypesBase);
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 [modCenterVals, setModCenterVals] = React.useState({ '1': {}, '2': {}, '3': {}, '4': {}, '5': {}, '6': {}});
const [ticks, setTicks] = React.useState(0);
const [beatsInMeasure, setBeatsInMeasure] = React.useState(4);
@@ -119,12 +133,12 @@ function MasterLfoHandler(){
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'));
const [enumDjParamArr, setEnumDjParamArr] = React.useState(Array(MAXENUMPOINTS).fill('NONE'));
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;
baseEnumBreakpoints[i][j] = j - 0.5;
}
}
const [enumBreakPoints, setEnumBreakPoints] = React.useState(baseEnumBreakpoints);
@@ -132,7 +146,7 @@ function MasterLfoHandler(){
const getBlankEnumBreakPointRow = () => {
let arr = []
for (let i = 0; i < MAXENUMPOINTS + 1; i++)
arr.push(i)
arr.push(i - 0.5)
return arr;
}
@@ -242,7 +256,7 @@ function MasterLfoHandler(){
function handleTick(event) {
let time = (Date.now() - firstUpdateTime) / 1000;
let noiseData = { lastPhaseArr, setLastPhaseArr, cachedNoiseValueArr1, setCachedNoiseValueArr1, cachedNoiseValueArr2, setCachedNoiseValueArr2, noiseTypeArr };
operateModulators(modVisibleArr, modTypeArr, modInstanceNumArr, djParamArr, modCenterVals, freqArr, minArr, maxArr, shapeArr, initPhaseArr, noiseData, userDefinedWave, time, beatsInMeasure, ticks);
operateModulators(modVisibleArr, modTypeArr, modInstanceNumArr, djParamArr, modCenterVals, freqArr, minArr, maxArr, shapeArr, initPhaseArr, noiseData, userDefinedWaves, userDefinedFunctions, userDefinedTypes, time, beatsInMeasure, ticks);
}
function handleTimeSig(event) {
@@ -250,13 +264,29 @@ function MasterLfoHandler(){
}
function handleChangeUserWave(event) {
setUserDefinedWave(event.detail);
userDefinedWaves[event.detail.index] = event.detail.points;
setUserDefinedWaves(userDefinedWaves);
}
function handleChangeUserFunction(event) {
userDefinedFunctions[event.detail.index] = event.detail.points;
setUserDefinedFunctions(userDefinedFunctions);
}
function handleChangeUserDefinedType(event) {
userDefinedTypes[event.detail.index - 1] = event.detail.type;
setUserDefinedTypes(userDefinedTypes);
}
function handleMaxTicks(event) {
setTicks(event.detail);
}
function handleChangeViewMode(event){
setEnabled(true);
setViewMode(event.detail);
}
function setNN(event) {
for (let i = 0; i < MAXLFOS; i++) {
@@ -280,6 +310,15 @@ function MasterLfoHandler(){
}
setInitPhaseArr(initPhaseArr);
for (let i = MAXLFOS * 4; i < MAXLFOS * 5; i++) {
let index = i - MAXLFOS * 4;
let inst = modInstanceNumArr[index];
let param = djParamArr[index];
modCenterVals[inst][param] = parseFloat(event.detail[i]);
}
setModCenterVals(modCenterVals);
rerender(!render); // BAD! SHOULD NOT BE DOING THIS!
}
@@ -291,6 +330,17 @@ function MasterLfoHandler(){
allNNData = allNNData.concat(minArr);
allNNData = allNNData.concat(maxArr);
allNNData = allNNData.concat(initPhaseArr);
let lfoMatchedCenterVals = [];
for (let i = 0; i < MAXLFOS; i++) {
let inst = modInstanceNumArr[i];
let param = djParamArr[i];
lfoMatchedCenterVals.push(modCenterVals[inst][param]);
if (!lfoMatchedCenterVals[i])
lfoMatchedCenterVals[i] = 0
}
allNNData = allNNData.concat(lfoMatchedCenterVals);
window.max.outlet("NNdata " + allNNData.join(" "));
}
@@ -304,7 +354,10 @@ function MasterLfoHandler(){
window.addEventListener('enum', handleEnum);
window.addEventListener('timesig', handleTimeSig);
window.addEventListener('userWave', handleChangeUserWave);
window.addEventListener('userFunction', handleChangeUserFunction);
window.addEventListener('userDefinedType', handleChangeUserDefinedType);
window.addEventListener('maxTicks', handleMaxTicks);
window.addEventListener('viewMode', handleChangeViewMode);
return () => {
window.removeEventListener('loadDict', handleLoad);
@@ -316,9 +369,12 @@ function MasterLfoHandler(){
window.removeEventListener('enum', handleEnum);
window.removeEventListener('timesig', handleTimeSig);
window.removeEventListener('userWave', handleChangeUserWave);
window.removeEventListener('userFunction', handleChangeUserFunction);
window.removeEventListener('userDefinedType', handleChangeUserDefinedType);
window.removeEventListener('maxTicks', handleMaxTicks);
window.removeEventListener('viewMode', handleChangeViewMode);
};
}, [...allModArrays, ...allEnumArrays, ...allEnumMats, userDefinedWave, modCenterVals, render, beatsInMeasure, ticks]);
}, [...allModArrays, ...allEnumArrays, ...allEnumMats, userDefinedWaves, userDefinedFunctions, userDefinedTypes, modCenterVals, render, beatsInMeasure, ticks]);
function CheckLinked(inst, param, checkInstArr, checkParamArr) {
@@ -396,6 +452,7 @@ function MasterLfoHandler(){
allModSetters[j](array);
}
}
SendSaveEvent();
rerender(!render);
}
@@ -405,6 +462,7 @@ function MasterLfoHandler(){
let newArr = modVisibleArr.slice();
newArr[id] = false;
setModVisibleArr(newArr);
SendSaveEvent();
}
}
@@ -428,9 +486,15 @@ function MasterLfoHandler(){
enumItems: enumItemCounts[i],
setEnumItemCounts: CreateParamChanger(enumItemCounts, setEnumItemCounts, i),
enumBreakPoints: enumBreakPoints,
setEnumBreakPoints: setEnumBreakPoints,
setEnumBreakPoints: (val) => {
setEnumBreakPoints(val);
SendSaveEvent
},
enumNames: enumNames,
setEnumNames: setEnumNames,
setEnumNames: (val) => {
setEnumNames(val);
SendSaveEvent
},
visible: enumVisibleArr[i],
djParam: enumDjParamArr[i],
setDjParam: CreateParamChanger(enumDjParamArr, setEnumDjParamArr, i),
@@ -476,6 +540,7 @@ function MasterLfoHandler(){
}
}
rerender(!render);
SendSaveEvent();
}
},
removeEnum: () => {
@@ -483,6 +548,7 @@ function MasterLfoHandler(){
let newArr = enumVisibleArr.slice();
newArr[id] = false;
setEnumVisibleArr(newArr);
SendSaveEvent();
}
}
}, null)
@@ -491,16 +557,19 @@ function MasterLfoHandler(){
var grid;
var title;
var modButtonClass;
var enumButtonClass;
var labels;
if (viewMode === ViewModes.MOD) {
grid = modContents;
title = "MODULATORS";
modButtonClass = "highlighted-button";
enumButtonClass = "unhighlighted-button";
labels = MODULATORLABELS;
}
else {
grid = enumContents;
title = "ENUMERATORS";
modButtonClass = "unhighlighted-button";
enumButtonClass = "highlighted-button";
labels = ENUMERATORLABELS;
}
@@ -513,13 +582,22 @@ function MasterLfoHandler(){
}
return e('div', null,
e('div', {className: 'container'},
e(Switch, {ontoggle: toggleViewMode}, null),
e('span', {className: lockClass, onClick: toggleLockMode}, null)),
e('div', { className: 'header' },
e('div', {className: 'nav'},
displayIfEnabled(e('button', { onClick: () => setViewMode(ViewModes.MOD), className: modButtonClass}, 'Modulators')),
displayIfEnabled(e('button', { onClick: () => setViewMode(ViewModes.ENUM), className: enumButtonClass }, 'Enumerators'))
),
e('button', { onClick: toggleEnabled, id: 'hide-button'}, toggleEnabledText),
e('h5', null, title),
e('ul', null, ...labels.map(x => ListItem(Label(x)))),
e('div', {id: 'grid'}, ...grid)
//allows lock mode
//e('span', { className: lockClass, onClick: toggleLockMode }, null)
),
displayIfEnabled(
e('table', { id: 'table' },
e('thead', null, e('tr', { id: 'headers' }, ...labels.map(x => e('th', {id: x == '# points' ? 'points' : x}, x)))),
e('tbody', null, ...grid)
))
);
}
@@ -541,7 +619,6 @@ if (!DEBUG){
});
window.max.bindInlet("param", (inst, paramName, val) => {
window.dispatchEvent(new CustomEvent('param', { 'detail': [inst, paramName, val] }));
});
@@ -553,10 +630,26 @@ if (!DEBUG){
window.dispatchEvent(new CustomEvent('maxTicks', { 'detail': val }));
});
window.max.bindInlet("userWave", (...points) => {
window.dispatchEvent(new CustomEvent('userWave', {'detail' : points}));
window.max.bindInlet("userWave", (index, ...points) => {
let data = { points, index };
window.dispatchEvent(new CustomEvent('userWave', { 'detail': data }));
});
window.max.bindInlet("viewMode", (mode) => {
window.dispatchEvent(new CustomEvent('viewMode', { 'detail': parseInt(mode) }));
});
window.max.bindInlet("userFunction", (index, ...points) => {
//list of 101 points between 0-100
let data = { points, index };
window.dispatchEvent(new CustomEvent('userFunction', { 'detail': data }));
});
window.max.bindInlet("userDefinedType", (index, type) => {
let data = { index, type };
window.dispatchEvent(new CustomEvent('userDefinedType', { 'detail': data }));
})
setInterval(() => {
window.dispatchEvent(new CustomEvent('tick'));
}, 200);

View File

@@ -4,10 +4,10 @@
var TYPEOPTIONS = ["LFO", "Noise"];
var SHAPETYPES = ["Sine", "SawUp", "SawDown", "Tri", "Square", "Custom"];
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"];
var INSTANCEOPTIONS = ["1", "2", "3", "4", "5", "6"];
const MODPARAMOPTIONS = ["NONE", "stream", "pulse_length", "eventfulness", "event_length", "metriclarity",
"harmoniclarity", "melodic_cohesion", "melody_scope", "tonic_pitch", "pitch_center", "pitch_range", "dynamics",
@@ -22,7 +22,9 @@ function ControlType(){
return e('select', {className: 'control-type'}, Option("LFO"));
}
function DataCell(element) {
return e('td', null, element);
}
function LfoRow(props){
@@ -34,34 +36,42 @@ function LfoRow(props){
let typeOption = null;
if (props.type == "LFO"){
typeOption = ListItem(DropDown({locked:props.locked, onChange: props.setShape, value:props.shape, options: SHAPETYPES}));
typeOption = DataCell(DropDown({locked:props.locked, onChange: props.setShape, value:props.shape, options: SHAPETYPES}));
}
else if (props.type == "Noise"){
typeOption = ListItem(DropDown({locked:props.locked, onChange: props.setNoise, value:props.noise, options: NOISETYPES}));
typeOption = DataCell(DropDown({locked:props.locked, onChange: props.setNoise, value:props.noise, options: NOISETYPES}));
}
let content = e('ul', {className: 'lfo-item'},
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})),
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,
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)),
//ListItem(e(NumberBox, {onChange:props.setAmp, value:props.amp, step:0.1}, null)),
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, locked: props.locked}, null)),
ListItem(e(Button, {text:'-', onClick: props.removeLfo, locked: props.locked}, null)),
ListItem(e("div", {className:"linked"}, linkedText)),
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
return content;
};
}
function indexWave(type, phase, userDefinedWave){
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;
@@ -73,12 +83,18 @@ function indexWave(type, phase, userDefinedWave){
return phase > 0.5? (1-phase) * 2 : phase * 2;
case "Square":
return +(phase > 0.5);
case "Custom":
return parseFloat(userDefinedWave[Math.floor(phase * 50)]) / 127
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, userDefinedWave, currTime, beatsInMeasure, ticks){
function operateModulators(visibleArr, typeArr, instanceNumArr, paramNames, centers, freqs, mins, maxs, waveTypes, phaseArr, noiseData, userDefinedWaves, userDefinedFunctions, userDefinedTypes, currTime, beatsInMeasure, ticks){
for (let i=0; i<paramNames.length; i++){
if (visibleArr[i]){
@@ -92,7 +108,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);
output = operateLFO(center, inst, freqs[i], mins[i], maxs[i], waveTypes[i], phaseArr, i, userDefinedWaves, userDefinedFunctions, userDefinedTypes, name, currTime, beatsInMeasure, ticks);
else
output = operateNoise(center, inst, freqs[i], mins[i], maxs[i], waveTypes[i], phaseArr, i, name, noiseData, currTime, beatsInMeasure, ticks);
if (name !== "NONE")
@@ -101,7 +117,7 @@ function operateModulators(visibleArr, typeArr, instanceNumArr, paramNames, cent
}
}
function operateLFO(center, inst, timeBaseStr, min, max, waveType, phaseArr, phaseIndex, userDefinedWave, name, currTime, beatsInMeasure, maxTicks){
function operateLFO(center, inst, timeBaseStr, min, max, waveType, phaseArr, phaseIndex, userDefinedWaves, userDefinedFunctions, userDefinedTypes, name, currTime, beatsInMeasure, maxTicks){
let amp = parseFloat(max) - parseFloat(min);
let phaseType;
let timeBase;
@@ -113,7 +129,7 @@ function operateLFO(center, inst, timeBaseStr, min, max, waveType, phaseArr, pha
phase = (currTime * timeBase + parseFloat(phaseArr[phaseIndex])) % 1.00;
else if (phaseType === PhaseTypes.MUSICAL)
phase = (maxTicks % timeBase) / timeBase;
let unscaled = indexWave(waveType, phase, userDefinedWave);
let unscaled = indexWave(waveType, phase, userDefinedWaves, userDefinedFunctions, userDefinedTypes);
syncDisplay(inst, name, unscaled);
return unscaled * amp + center + parseFloat(min);
@@ -142,10 +158,9 @@ function operateNoise(center, inst, timeBaseStr, min, max, waveType, phaseArr, i
else if (phaseType === PhaseTypes.MUSICAL)
phase = (maxTicks % timeBase) / timeBase;
if (noiseData.cachedNoiseValueArr1[index] == 0 || noiseData.lastPhaseArr[index] > phase){ // occurs if the phase reset to 0 or at the very start
noiseData.cachedNoiseValueArr1[index] = noiseData.cachedNoiseValueArr2[index];
noiseData.cachedNoiseValueArr2[index] = noiseData.cachedNoiseValueArr1[index];
if (noiseData.cachedNoiseValueArr1[index] == 0)
noiseData.cachedNoiseValueArr2[index] = center;
@@ -156,14 +171,11 @@ function operateNoise(center, inst, timeBaseStr, min, max, waveType, phaseArr, i
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(noiseType, noiseData.cachedNoiseValueArr1[index], noiseData.cachedNoiseValueArr2[index], phase);
syncDisplay(inst, name, unscaled);
return unscaled * amp + center + parseFloat(min);
}
function interpolateNoise(type, cachedVal1, cachedVal2, phase){

93
myStorage.json Normal file

File diff suppressed because one or more lines are too long