functional modulators

This commit is contained in:
trian-gles 2024-07-18 15:55:00 +02:00
parent b206270087
commit 72858f0959
5 changed files with 190 additions and 16 deletions

View File

@ -12,7 +12,7 @@ function Label(text){
}
function NumberBox(props){
return e('input', {type: "number", onChange: props.onChange, value: props.value, className: props.className}, null);
return e('input', {type: "number", onChange: props.onChange, step: props.step, value: props.value, className: props.className}, null);
}
function TextBox(props){

View File

@ -18,8 +18,8 @@ function EnumeratorItems(index, enumBreakPoints, setEnumBreakPoints, enumNames,
function EnumeratorRow(props){
let content = e('ul', {className: 'lfo-item'},
ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: PARAMOPTIONS})),
ListItem(e(NumberBox, {onChange: props.setEnumItemCounts, 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]}, null)),
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).slice(0, props.enumItems * 2)),
ListItem(e(Button, {text:'+', onClick: props.addEnum}, null)),
ListItem(e(Button, {text:'-', onClick: props.removeEnum}, null))

View File

@ -3,14 +3,14 @@
"fileversion" : 1,
"appversion" : {
"major" : 8,
"minor" : 5,
"revision" : 6,
"minor" : 6,
"revision" : 2,
"architecture" : "x64",
"modernui" : 1
}
,
"classnamespace" : "box",
"rect" : [ 495.0, 87.0, 815.0, 715.0 ],
"rect" : [ 292.0, 100.0, 799.0, 715.0 ],
"bglocked" : 0,
"openinpresentation" : 0,
"default_fontsize" : 12.0,
@ -39,6 +39,54 @@
"subpatcher_template" : "",
"assistshowspatchername" : 0,
"boxes" : [ {
"box" : {
"id" : "obj-17",
"linecount" : 2,
"maxclass" : "comment",
"numinlets" : 1,
"numoutlets" : 0,
"patching_rect" : [ 520.0, 441.0, 150.0, 34.0 ],
"text" : "question for Georg: what should `phase` do?"
}
}
, {
"box" : {
"id" : "obj-13",
"maxclass" : "message",
"numinlets" : 2,
"numoutlets" : 1,
"outlettype" : [ "" ],
"patching_rect" : [ 130.0, 183.0, 88.0, 22.0 ],
"text" : "param pass 30"
}
}
, {
"box" : {
"id" : "obj-8",
"maxclass" : "message",
"numinlets" : 2,
"numoutlets" : 1,
"outlettype" : [ "" ],
"patching_rect" : [ 228.0, 294.0, 129.0, 22.0 ],
"text" : "param attenuation 200"
}
}
, {
"box" : {
"id" : "obj-5",
"maxclass" : "button",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "bang" ],
"parameter_enable" : 0,
"patching_rect" : [ 365.0, 582.0, 24.0, 24.0 ]
}
}
, {
"box" : {
"id" : "obj-16",
"maxclass" : "message",
@ -64,11 +112,12 @@
"id" : "obj-4",
"maxclass" : "newobj",
"numinlets" : 2,
"numoutlets" : 4,
"outlettype" : [ "dictionary", "", "", "" ],
"numoutlets" : 5,
"outlettype" : [ "dictionary", "", "", "", "" ],
"patching_rect" : [ 693.0, 69.0, 159.0, 22.0 ],
"saved_object_attributes" : {
"embed" : 1,
"legacy" : 1,
"parameter_enable" : 0,
"parameter_mappable" : 0
}
@ -95,7 +144,7 @@
"maxclass" : "comment",
"numinlets" : 1,
"numoutlets" : 0,
"patching_rect" : [ 438.52631402015686, 27.5, 150.0, 33.0 ],
"patching_rect" : [ 438.52631402015686, 27.5, 150.0, 34.0 ],
"text" : "required due to the asynchronous operation"
}
@ -191,6 +240,13 @@
"source" : [ "obj-12", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-2", 0 ],
"source" : [ "obj-13", 0 ]
}
}
, {
"patchline" : {
@ -216,6 +272,15 @@
, {
"patchline" : {
"destination" : [ "obj-10", 0 ],
"order" : 1,
"source" : [ "obj-2", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-5", 0 ],
"order" : 0,
"source" : [ "obj-2", 0 ]
}
@ -242,8 +307,28 @@
"source" : [ "obj-3", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-2", 0 ],
"source" : [ "obj-8", 0 ]
}
}
],
"parameters" : {
"parameterbanks" : {
"0" : {
"index" : 0,
"name" : "",
"parameters" : [ "-", "-", "-", "-", "-", "-", "-", "-" ]
}
}
,
"inherited_shortname" : 1
}
,
"dependency_cache" : [ ],
"autosave" : 0
}

View File

@ -20,6 +20,10 @@ const ViewModes = Object.freeze({
ENUM: 1
});
var modPhases = Array(MAXLFOS).fill(0);
var lastUpdateTime = Date.now();
const MODULATORLABELS = ["-type-", "---shape---", "----param----", "freq", "amp", "phase"];
const ENUMERATORLABELS = ["--parameter--", "-points-"];
@ -41,6 +45,8 @@ function MasterLfoHandler(){
const [modVisibleArr, setModVisibleArr] = React.useState(initVisArr);
const [modCenterVals, setModCenterVals] = React.useState({});
const [shapeArr, setShapeArr] = React.useState(Array(MAXLFOS).fill('Sine'));
const [djParamArr, setDjParamArr] = React.useState(Array(MAXLFOS).fill('attenuation'));
@ -114,16 +120,50 @@ function MasterLfoHandler(){
window.max.setDict(event.detail, {"data" : data});
}
function handleParam(event) {
let name = event.detail[0];
let val = event.detail[1];
// if none of the LFOs use this param, then we output it raw
let i = 0;
while (i < MAXLFOS){
if (modVisibleArr[i] && djParamArr[i] == name)
break;
i++
}
if (i == MAXLFOS){
window.max.outlet(name + ' ' + val);
}
modCenterVals[name] = val;
setModCenterVals(modCenterVals);
}
function handleTick(event) {
let newTime = Date.now()
let delta = (newTime - lastUpdateTime) / 1000;
lastUpdateTime = newTime
operateModulators(modVisibleArr, djParamArr, modCenterVals, freqArr, ampArr, shapeArr, modPhases, delta);
}
window.addEventListener('loadDict', handleLoad);
window.addEventListener('saveDict', handleSave);
window.addEventListener('tick', handleTick);
window.addEventListener('param', handleParam);
return () => {
window.removeEventListener('loadDict', handleLoad);
window.removeEventListener('saveDict', handleSave);
window.removeEventListener('tick', handleTick);
window.removeEventListener('param', handleParam);
};
}, [...allModArrays, ...allEnumArrays, ...allEnumMats]);
}, [...allModArrays, ...allEnumArrays, ...allEnumMats, modCenterVals]);
@ -288,7 +328,15 @@ if (!DEBUG){
window.max.bindInlet("save", (dictId) => {
window.dispatchEvent(new CustomEvent('saveDict', {'detail' : dictId}));
})
});
window.max.bindInlet("param", (paramName, val) => {
window.dispatchEvent(new CustomEvent('param', {'detail' : [paramName, val]}));
});
setInterval(() => {
window.dispatchEvent(new CustomEvent('tick'));
}, 200);
}

View File

@ -3,7 +3,9 @@
/////////////////////////
var SHAPETYPES = ["Sine", "SawUp", "SawDown", "Tri", "Square"];
const PARAMOPTIONS = ["attenuation", "melody_scope"];
const PARAMOPTIONS = ["pulse_length", "eventfulness", "event_length", "metriclarity",
"harmoniclarity", "melodic_cohesion", "melody_scope", "tonic_pitch", "pitch_center", "pitch_range", "dynamics",
"attenuation", "chordal_weight"]
function ControlType(){
return e('select', {className: 'control-type'}, Option("LFO"));
@ -16,13 +18,52 @@ function LfoRow(props){
ListItem(ControlType()),
ListItem(DropDown({onChange: props.setShape, value:props.shape, options: SHAPETYPES})),
ListItem(DropDown({onChange: props.setDjParam, value: props.djParam, options: PARAMOPTIONS})),
ListItem(e(NumberBox, {onChange:props.setFreq, value:props.freq}, null)),
ListItem(e(NumberBox, {onChange:props.setAmp, value:props.amp}, null)),
ListItem(e(NumberBox, {onChange:props.setPhase, value:props.phase}, null)),
ListItem(e(NumberBox, {onChange:props.setFreq, value:props.freq, 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(Button, {text:'+', onClick: props.addLfo}, null)),
ListItem(e(Button, {text:'-', onClick: props.removeLfo}, null))
);
if (props.visible){
return content
};
}
function indexWave(type, phase){
switch (type){
case "Sine":
return Math.sin(phase * Math.PI * 2);
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);
}
}
function operateModulators(visibleArr, paramNames, centers, freqs, amps, waveTypes, phaseArr, delta){
for (let i=0; i<paramNames.length; i++){
if (visibleArr[i]){
let name = paramNames[i];
let center = 0;
if (centers.hasOwnProperty(name)){
center = centers[name];
}
let output = operateModulator(name, center, freqs[i], amps[i], waveTypes[i], phaseArr, i, delta);
window.max.outlet(output);
}
}
}
function operateModulator(paramName, center, freq, amp, waveType, phaseArr, phaseI, delta){
let oldPhase = phaseArr[phaseI];
let newPhase = (oldPhase + freq * delta) % 1.00;
phaseArr[phaseI] = newPhase;
let outputVal = indexWave(waveType, newPhase) * amp + center;
return paramName + ' ' + outputVal.toString();
}