Skip to content

Commit 2dffbcb

Browse files
committed
midi stuff
1 parent 29a833f commit 2dffbcb

13 files changed

Lines changed: 714 additions & 2 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const
2+
exec = op.inTriggerButton("Trigger"),
3+
inData = op.inString("DataUrl", ""),
4+
result = op.outNumber("Result");
5+
6+
exec.onTriggered = () =>
7+
{
8+
if (CABLESUILOADER && CABLESUILOADER.talkerAPI)
9+
CABLESUILOADER.talkerAPI.send(CABLESUILOADER.TalkerAPI.CMD_SAVE_PATCH_SCREENSHOT,
10+
{ "screenshot": inData.get() }, (err, r) =>
11+
{
12+
console.log("screenshot", err, r);
13+
});
14+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"id": "42a811bb-48ab-45ee-b0fb-3113c43dc9d1",
3+
"created": 1764149495221,
4+
"license": "MIT",
5+
"authorName": "pandur",
6+
"changelog": [
7+
{
8+
"message": "op created",
9+
"type": "new op",
10+
"author": "pandur",
11+
"date": 1764149495258
12+
}
13+
],
14+
"layout": {
15+
"portsIn": [
16+
{
17+
"type": 1,
18+
"name": "Trigger"
19+
},
20+
{
21+
"type": 5,
22+
"name": "DataUrl"
23+
}
24+
],
25+
"portsOut": [
26+
{
27+
"type": 0,
28+
"name": "Result",
29+
"subType": "number"
30+
}
31+
]
32+
}
33+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const MIDIChannels = Array.from(Array(16).keys(), (i) => { return i + 1; }),
2+
clamp = (val, min, max) => { return Math.min(Math.max(val, min), max); },
3+
CC_STATUS_BYTE = 0xb;
4+
5+
const inExec = op.inTriggerButton("Send"),
6+
inChannel = op.inDropDown("MIDI Channel", MIDIChannels, "1"),
7+
inCcIndex = op.inInt("CC Index", 1),
8+
inCCValue = op.inInt("CC Value", 0),
9+
inAuto = op.inBool("Auto send value change", false),
10+
outEvent = op.outObject("MIDI Event Out");
11+
12+
inExec.onTriggered = send;
13+
14+
inCCValue.onChange = () =>
15+
{
16+
if (inAuto.get())send();
17+
};
18+
19+
function send()
20+
{
21+
const ccValue = clamp(inCCValue.get(), 0, 127);
22+
const ccIndex = clamp(inCcIndex.get(), 1, 127);
23+
24+
const event = {
25+
"deviceName": null,
26+
"output": null,
27+
"inputId": 0,
28+
"messageType": "CC",
29+
"data": [CC_STATUS_BYTE << 4 | (inChannel.get() - 1), ccIndex, ccValue],
30+
"index": ccIndex,
31+
"value": ccValue,
32+
"channel": inChannel.get() - 1,
33+
};
34+
35+
outEvent.setRef(event);
36+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{
2+
"id": "c38be648-ab6e-401f-82a0-89a4a29fb8d7",
3+
"changelog": [
4+
{
5+
"message": "created op",
6+
"author": "simod",
7+
"date": 1571227742167
8+
},
9+
{
10+
"message": "Ops.User.simod.MidiCCOut renamed to Ops.Devices.Midi.MidiCCOut",
11+
"author": "simod",
12+
"date": 1571309165529
13+
}
14+
],
15+
"authorName": "simod",
16+
"created": 1764247626977,
17+
"layout": {
18+
"portsIn": [
19+
{
20+
"type": 1,
21+
"name": "Send"
22+
},
23+
{
24+
"type": 0,
25+
"name": "MIDI Channel index",
26+
"subType": "integer"
27+
},
28+
{
29+
"type": 0,
30+
"name": "CC Index",
31+
"subType": "integer"
32+
},
33+
{
34+
"type": 0,
35+
"name": "CC Value",
36+
"subType": "integer"
37+
},
38+
{
39+
"type": 0,
40+
"name": "Auto send value change",
41+
"subType": "boolean"
42+
}
43+
],
44+
"portsOut": [
45+
{
46+
"type": 2,
47+
"name": "MIDI Event Out"
48+
}
49+
]
50+
},
51+
"summary": "send MIDI CC data to a midi output",
52+
"issues": "",
53+
"youtubeids": [],
54+
"docs": {
55+
"ports": [
56+
{
57+
"name": "MIDI Channel",
58+
"text": "the midi channel of the CC message"
59+
},
60+
{
61+
"name": "CC Index",
62+
"text": "the index of the CC message"
63+
},
64+
{
65+
"name": "CC Value",
66+
"text": "the value of the CC message"
67+
},
68+
{
69+
"name": "Min In Value",
70+
"text": "the minimum value that is coming in"
71+
},
72+
{
73+
"name": "Max In Value",
74+
"text": "the maximum value that is coming in"
75+
},
76+
{
77+
"name": "MIDI Event Out",
78+
"text": "the midi event output port"
79+
}
80+
]
81+
},
82+
"exampleProjectId": "VbaQXU",
83+
"license": "MIT"
84+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Use this op to create MIDI CC data to send out to your MIDI device.
2+
3+
The value and index can be animated to send different CCs via a MIDI channel.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
const MIDIChannels = Array.from(Array(16).keys(), (i) => { return i + 1; });
2+
3+
const
4+
inEvent = op.inObject("MIDI Event In"),
5+
midiChannelDropdown = op.inValueSelect("MIDI Channel", MIDIChannels, 1),
6+
ccIndexDropdown = op.inValueInt("CC Index", 0),
7+
inSpeed = op.inFloat("Speed", 1),
8+
normalizeDropdown = op.inSwitch("Normalize", ["none", "0 to 1", "-1 to 1"], "none"),
9+
triggerOn = op.inSwitch("Trigger On", ["Both", "Down", "Up"], "Both"),
10+
learn = op.inTriggerButton("learn"),
11+
clear = op.inTriggerButton("clear");
12+
13+
const
14+
ccValueOut = op.outNumber("CC Value Out"),
15+
eventOut = op.outObject("Event"),
16+
triggerOut = op.outTrigger("Trigger Out"),
17+
ccIndexOut = op.outNumber("CC Index Out");
18+
19+
ccIndexDropdown.set(0);
20+
midiChannelDropdown.set(1);
21+
normalizeDropdown.set(normalizeDropdown.get("none"));
22+
23+
let learning = false;
24+
learn.onTriggered = () =>
25+
{
26+
learning = true;
27+
};
28+
29+
clear.onTriggered = () =>
30+
{
31+
ccIndexDropdown.set(0);
32+
midiChannelDropdown.set(1);
33+
normalizeDropdown.set(normalizeDropdown.get("none"));
34+
op.refreshParams();
35+
};
36+
37+
inEvent.onChange = () =>
38+
{
39+
const event = inEvent.get();
40+
if (!event) return;
41+
if (event.messageType !== "CC") return;
42+
43+
let [, ccIndex, ccValue] = event.data;
44+
if (learning)
45+
{
46+
ccIndexDropdown.set(ccIndex);
47+
midiChannelDropdown.set(event.channel + 1);
48+
49+
learning = false;
50+
51+
if (CABLES.UI)
52+
{
53+
gui.emitEvent("portValueEdited", op, ccIndexDropdown, ccIndexDropdown.get());
54+
gui.emitEvent("portValueEdited", op, midiChannelDropdown, midiChannelDropdown.get());
55+
56+
op.uiAttr({ "info": `bound to CC: ${ccIndexDropdown.get()}` });
57+
op.refreshParams();
58+
}
59+
}
60+
61+
if (event.channel === midiChannelDropdown.get() - 1)
62+
{
63+
if (ccIndex === ccIndexDropdown.get())
64+
{
65+
ccIndexOut.set(ccIndex);
66+
67+
if (normalizeDropdown.get() === "0 to 1")
68+
{
69+
const value = ccValue / 127;
70+
ccValueOut.set(value);
71+
72+
if (triggerOn.get() == "Both") triggerOut.trigger();
73+
else if (triggerOn.get() == "Down" && ccValue != 0) triggerOut.trigger();
74+
else if (triggerOn.get() == "Up" && ccValue == 0) triggerOut.trigger();
75+
}
76+
else if (normalizeDropdown.get() === "-1 to 1")
77+
{
78+
const value = ccValue / (127 / 2) - 1;
79+
80+
ccValueOut.set(value);
81+
82+
if (triggerOn.get() == "Both") triggerOut.trigger();
83+
else if (triggerOn.get() == "Down" && ccValue != 0) triggerOut.trigger();
84+
else if (triggerOn.get() == "Up" && ccValue == 0) triggerOut.trigger();
85+
}
86+
else if (normalizeDropdown.get() === "none")
87+
{
88+
ccValueOut.set(ccValue);
89+
90+
if (triggerOn.get() == "Both") triggerOut.trigger();
91+
else if (triggerOn.get() == "Down" && ccValue != 0) triggerOut.trigger();
92+
else if (triggerOn.get() == "Up" && ccValue == 0) triggerOut.trigger();
93+
}
94+
else
95+
{
96+
ccValue.set(0);
97+
}
98+
}
99+
}
100+
101+
eventOut.setRef(event);
102+
};

0 commit comments

Comments
 (0)