I currently use an Icon Plaform M+ with its display unit option (DU) with GP. This one seems to have the same small form factor and has also an optional OLED display unit:
It could become my new surface control.
I currently use an Icon Plaform M+ with its display unit option (DU) with GP. This one seems to have the same small form factor and has also an optional OLED display unit:
It could become my new surface control.
I see thereās an even smaller ānanoā version as well.
I like what theyāve done with the 4x4 touch button grid. After the holiday Iāll email them and ask if there is a way to change the labels on those via sysex rather than having to go through the IMAP program every time. Would be very cool if they could be changed in real time through GP scripting/extensions.
The display is definitely an upgrade. I never really liked the display on the Platform M+. It works, but itās not great. The OLED display looks much nicer.
Given that the form factor is the same as the M+ Iām not sure itās worth an upgrade for me. I may grab the P1-Nano though, being roughly half the width. I also really like that the nano will work on USB power (assuming your PC is up to it).
From the documentation it looks like everything operates on MCU protocol (like most of Iconās other controllers). Thatās great for compatibility, but also defines (limits) what you can display. Iād love to get more than 7 characters per channel strip, but at least on this one each scribble area is distinct so you can use all 7 rather than having to leave a space at the end (thus 6 chars) so they donāt all run together.
I also wanted to contact them, so please if you do so, share your info here.
Not very useful update, but I emailed Icon to ask if there was a way to re-write the button labels on the touch screen using sysex or any other means.
They got back to me pretty quickly, asking me to elaborate on what I had in mind and what the use case would be.
I wrote a fairly detailed response a week ago and have not heard back. I suppose hearing nothing is better than a ānoā but not as good as a āyesā.
Hi Vindes.
Curious if you ever heard back from Icon?
I too would be interested in this
Thanks in advance.
No, never heard back after their initial reply.
As the P1-M is set up from a communications perspective, Iām not sure if what I was hoping for is possible. I hoped to do some testing and potentially reverse engineer a protocol if I could snoop on the communications between the PC and the device.
If it was all happening over MIDI then it might be a worthwhile endeavor. That doesnāt appear to be the case. It looks like anything that can change what appears on the buttons has to go over USB rather than MIDI (because Iām not seeing it in the midi), and I donāt have the time to go down that path if the company isnāt interested in going there.
Iām not writing it off as impossible. When I first started communicating with Novation about the protocols to communicate with the SL-MK3 line I also ran into a wall. However, I knew at the time that the possibility existed because others were doing it in Bitwig and Ableton. I decompiled their code and was able to figure out much of the protocol. Shortly after that Novation finally publicly released the communication details. (I assume they were available to others under non-disclosure agreements before that.)
Iām not holding my breath for anything new from Icon on this front, but maybe when they have time theyāll decide itās worth enabling or supporting.
Thanks for your extended answer.
Too bad Icon didnāt get back. That controller has so much potential to have those displays being dynamically populated etc and being able to change them through scripts in your daw.
Iām mainly in reaper and doing a lot of scripting, and with the deep integration there you could make something remarkable (already have lots of stuff happening with the Icon Playform M+).
But sounds like you have lots of experience with these approaches and the fact you donāt see any data happening on the midi port when editing the iMap software is a downer.
I still think the device will be worth it for me because of the bigger screen estate and the faders seems nicer. Maybe eventually there could be a way to also manipulate the lcd panel.
Iāll also write icon so they know itās not a standalone request
Thanks again.
Good news, all you wanted is possible!!
So I also wrote them and got this answer:
Which is obviously not true, as you literally write the text on the D4T display with sysex messages when connected to Mackie for instance.
So Iād say they are just not interested in users doing this!
ANYWAY, I got my unit 2 days ago, and have been able to decode both the display (and the extra space available) as wall as the 16 LCD display buttons. And itās possible to write all of it with sysex.
The LCD display is written via port 4, but writes the entire units entire MIDI setup (so quite a big Sysex dump), so itās not the simplest thing, and you canāt just overwrite one buttons text.
The extra two lines on the D4T display is a bit weird. They also only support 7 characters pr. line and needs the full text of all 8 channels to work reliably, and is written through the (DAW) port you want to use.
So Iāve slowly started implementing some of the things including the color strip and making a very nice custom setup:
Hereās a picture of it all, and you can see the āautomatedā names on the LCD display:
The youtube link is āno moreā available for me. Could you please share your findings?
Thatās great! The main two lines of text on the D4T display I had working, as thatās just the standard Mackie MCU protocol. All the faders, knobs, and buttons follow the MCU protocol.
The reason I bought the thing (mine is the 1 fader nano version) was for the possibility to control the text on the buttons.
Iād love to see the details of how you write to those.
Sorry for the confusion, thatās from the email screenshot from Icon.
Itās just their product video, so not useful at all. Iām working in reaper, using Lua script, and still decoding things. So kinda hard to share the exact codes.
I used MIDI Monitor: snoize: MIDI Monitor to figure out what messages was sent to the icon mixer.
I might try and do a bigger write up when Iām less busy
So at this moment itās still early stages. Iāve used SoundFlow to ādecodeā and test how to write it as Iām more proficient in Javascript, but will later bring it in to Reaper as Lua scripts.
So it might not make any sense to you, but you can have the code.
The 3 first functions deals with trying to make the text look nice and how the iMap is formatting it. Thereās maybe still more work to do on that.
Iām unsure if itāll work if you try dump it on to the Nano, as it has less buttons and all that extra sysex code where Iāve written ā// add settings for deviceā is the settings for all the buttons and faders on the device.
But you could try. Also if you need it in another code language, ChatGPT is excellent at re-writing code to another language.
function decimalToHex(decimalNumber, padding = 2) {
let hex = decimalNumber.toString(16).toUpperCase();
while (hex.length < padding) {
hex = '0' + hex;
}
return hex;
}
function centerText(text, maxAmount = 8) {
let spaceAmount = maxAmount - text.length
let newText = text
for (let i = 0; i < spaceAmount; i++) {
if (i < Math.floor(spaceAmount / 2)) {
newText = " " + newText
}
else {
newText = newText + " "
}
}
return newText
}
function getCenterSpace(str) {
let indices = []
for (let i = 0; i < str.length; i++) {
if (str[i] === ' ') {
indices.push(i);
}
}
if (indices.length == 1 && indices[0] <= 8 && str.length - (indices[0] + 1) <= 8) {
return indices[0]
}
else if (indices.length > 1) {
return -2
}
else {
return -1
}
}
function textToHex(text) {
let hexValues = [];
let indexValue;
let fullText;
let firstPart;
let secondPart;
text = text.trim() // remove space before or after text
if (text.length < 8) {
indexValue = text.length % 2 === 0 ? "03" : "01";
fullText = centerText(text) + centerText("")
firstPart = centerText(text)
secondPart = centerText("")
}
else if (text.length == 8) {
indexValue = "02";
fullText = text + centerText("")
} else {
let spaceIndex = getCenterSpace(text)
if (spaceIndex == -1) {
firstPart = text.slice(0, 8);
secondPart = text.slice(8);
fullText = centerText(firstPart) + centerText(" " + secondPart)
}
else if (spaceIndex == -2) {
firstPart = text.slice(0, 8);
secondPart = (text.slice(8) + centerText("")).slice(0, 8);
fullText = centerText(firstPart) + centerText(secondPart)
}
else {
firstPart = text.slice(0, spaceIndex);
secondPart = text.slice(spaceIndex + 1);
fullText = centerText(firstPart) + centerText(secondPart)
}
// 04 both uneven
if (firstPart.length % 2 == 1 && secondPart.length % 2 == 1) {
indexValue = "04";
}
// 05 center top row
else if (firstPart.length % 2 == 1 && secondPart.length % 2 == 0) {
indexValue = "05";
}
// 06 center low row
else if (firstPart.length % 2 == 0 && secondPart.length % 2 == 1) {
indexValue = "06";
}
// 07 center both rows
else if (firstPart.length % 2 == 0 && secondPart.length % 2 == 0) {
indexValue = "07";
}
firstPart = centerText(firstPart)
secondPart = centerText(secondPart)
}
// Convert the text to an array of hex values
let hexText = indexValue + " " + fullText.split('').map(char => char.charCodeAt(0).toString(16).toUpperCase()).join(' ');
return hexText;
}
function setNames() {
// after 21, the next 3 values seems to be like a timestamp. end of string might be different for different daw modes
let introLine = "F0 1D 03 10 07 21 0E 58 32 6D 65 58 12 7A 03 F7\n"
let outroLines = "EF 7F 7F\n" + "EC 22 06\n" + "F0 1D 03 10 07 24 0E 58 32 6D 65 58 12 7A 03 F7"
// construct a string to set all fields to note on channel 2, starting from 0
let constructSendString = ""
let constructNameString = ""
for (let i = 0; i < 80; i++) {
if (i % 28 == 0) {
if (i > 0) { constructSendString = constructSendString + "F7\n" }
constructSendString = constructSendString + "F0 1D 03 10 07 25 06 0" + ((i / 28) + 1) + " 1C " + "09 09 00 " + decimalToHex(i+40) + " 7F 00 00 00 "
}
else {
constructSendString = constructSendString + "09 09 00 " + decimalToHex(i+40) + " 7F 00 00 00 "
}
let name = textToHex("test " + (i + 1))
if (i % 10 == 0) {
if (i > 0) { constructNameString = constructNameString + "F7\n" }
constructNameString = constructNameString + "F0 1D 03 10 07 26 06 0" + ((i / 10) + 1) + " 0A " + name + " "
}
else {
constructNameString = constructNameString + name + " "
}
}
// add settings for device
constructSendString = constructSendString +
"09 09 00 30 7F 00 00 00 09 09 00 31 7F 00 00 00 09 09 00 2E 7F 00 00 00 09 09 00 2F 7F 00 00 00 F7\n" +
"F0 1D 03 10 07 25 06 04 1C 09 09 00 32 7F 00 00 00 00 00 00 00 00 00 00 00 09 09 00 4F 7F 00 00 00 09 09 00 4A 7F 00 00 00 09 09 00 4C 7F 00 00 00 09 09 00 4B 7F 00 00 00 09 09 00 4E 7F 00 00 00 09 09 00 4D 7F 00 00 00 09 09 00 5B 7F 00 00 01 09 09 00 5C 7F 00 00 01 09 09 00 56 7F 00 00 01 09 09 00 5D 7F 00 00 01 09 09 00 5E 7F 00 00 01 09 09 00 5F 7F 00 00 01 09 09 00 00 7F 00 00 00 09 09 00 01 7F 00 00 00 09 09 00 02 7F 00 00 00 09 09 00 03 7F 00 00 00 09 09 00 04 7F 00 00 00 09 09 00 05 7F 00 00 00 09 09 00 06 7F 00 00 00 09 09 00 07 7F 00 00 00 09 09 00 20 7F 00 00 00 09 09 00 21 7F 00 00 00 09 09 00 22 7F 00 00 00 09 09 00 23 7F 00 00 00 09 09 00 24 7F 00 00 00 09 09 00 25 7F 00 00 00 F7\n" +
"F0 1D 03 10 07 25 06 05 1C 09 09 00 26 7F 00 00 00 09 09 00 27 7F 00 00 00 09 09 00 65 7F 00 00 00 0B 0B 00 10 40 00 00 00 0B 0B 00 11 40 00 00 00 0B 0B 00 12 40 00 00 00 0B 0B 00 13 40 00 00 00 0B 0B 00 14 40 00 00 00 0B 0B 00 15 40 00 00 00 0B 0B 00 16 40 00 00 00 0B 0B 00 17 40 00 00 00 0B 0B 00 3C 40 00 00 00 09 09 00 66 7F 00 00 00 09 09 00 67 7F 00 00 00 09 09 00 68 7F 00 00 00 09 09 00 69 7F 00 00 00 09 09 00 6A 7F 00 00 00 09 09 00 6B 7F 00 00 00 09 09 00 6C 7F 00 00 00 09 09 00 6D 7F 00 00 00 09 09 00 6E 7F 00 00 00 09 09 00 6F 7F 00 00 00 09 09 00 70 7F 00 00 00 0E 0E 00 00 7F 00 00 00 0E 0E 01 00 7F 00 00 00 0E 0E 02 00 7F 00 00 00 0E 0E 03 00 7F 00 00 00 0E 0E 04 00 7F 00 00 00 F7\n" +
"F0 1D 03 10 07 25 06 06 1C 0E 0E 05 00 7F 00 00 00 0E 0E 06 00 7F 00 00 00 0E 0E 07 00 7F 00 00 00 0E 0E 08 00 7F 00 00 00 09 09 00 60 7F 00 00 00 09 09 00 61 7F 00 00 00 09 09 00 62 7F 00 00 00 09 09 00 63 7F 00 00 00 40 05 00 00 00 00 00 00 40 04 0F 00 00 00 00 00 50 00 00 00 02 00 00 00 50 00 00 00 01 00 00 00 09 09 00 52 7F 00 00 00 09 09 00 53 7F 00 00 00 09 09 00 54 7F 00 00 00 09 09 00 55 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 09 00 64 7F 00 00 00 09 09 00 64 7F 00 00 00 09 09 00 08 7F 00 00 00 09 09 00 09 7F 00 00 00 09 09 00 0A 7F 00 00 00 09 09 00 0B 7F 00 00 00 F7\n" +
"F0 1D 03 10 07 25 06 07 1C 09 09 00 0C 7F 00 00 00 09 09 00 0D 7F 00 00 00 09 09 00 0E 7F 00 00 00 09 09 00 0F 7F 00 00 00 09 09 00 10 7F 00 00 00 09 09 00 11 7F 00 00 00 09 09 00 12 7F 00 00 00 09 09 00 13 7F 00 00 00 09 09 00 14 7F 00 00 00 09 09 00 15 7F 00 00 00 09 09 00 16 7F 00 00 00 09 09 00 17 7F 00 00 00 09 09 00 18 7F 00 00 00 09 09 00 19 7F 00 00 00 09 09 00 1A 7F 00 00 00 09 09 00 1B 7F 00 00 00 09 09 00 1C 7F 00 00 00 09 09 00 1D 7F 00 00 00 09 09 00 1E 7F 00 00 00 09 09 00 1F 7F 00 00 00 09 09 00 20 7F 00 00 00 09 09 00 21 7F 00 00 00 09 09 00 22 7F 00 00 00 09 09 00 23 7F 00 00 00 09 09 00 24 7F 00 00 00 09 09 00 25 7F 00 00 00 09 09 00 26 7F 00 00 00 09 09 00 27 7F 00 00 00 F7\n" +
"F0 1D 03 10 07 25 06 08 1C 0B 0B 00 10 40 00 00 00 0B 0B 00 11 40 00 00 00 0B 0B 00 12 40 00 00 00 0B 0B 00 13 40 00 00 00 0B 0B 00 14 40 00 00 00 0B 0B 00 15 40 00 00 00 0B 0B 00 16 40 00 00 00 0B 0B 00 17 40 00 00 00 09 09 00 20 7F 00 00 00 09 09 00 21 7F 00 00 00 09 09 00 22 7F 00 00 00 09 09 00 23 7F 00 00 00 09 09 00 24 7F 00 00 00 09 09 00 25 7F 00 00 00 09 09 00 26 7F 00 00 00 09 09 00 27 7F 00 00 00 0B 0B 00 10 40 00 00 00 0B 0B 00 11 40 00 00 00 0B 0B 00 12 40 00 00 00 0B 0B 00 13 40 00 00 00 0B 0B 00 14 40 00 00 00 0B 0B 00 15 40 00 00 00 0B 0B 00 16 40 00 00 00 0B 0B 00 17 40 00 00 00 09 09 00 20 7F 00 00 00 09 09 00 21 7F 00 00 00 09 09 00 22 7F 00 00 00 09 09 00 23 7F 00 00 00 F7\n" +
"F0 1D 03 10 07 25 06 09 1C 09 09 00 24 7F 00 00 00 09 09 00 25 7F 00 00 00 09 09 00 26 7F 00 00 00 09 09 00 27 7F 00 00 00 0B 0B 00 10 40 00 00 00 0B 0B 00 11 40 00 00 00 0B 0B 00 12 40 00 00 00 0B 0B 00 13 40 00 00 00 0B 0B 00 14 40 00 00 00 0B 0B 00 15 40 00 00 00 0B 0B 00 16 40 00 00 00 0B 0B 00 17 40 00 00 00 09 09 00 20 7F 00 00 00 09 09 00 21 7F 00 00 00 09 09 00 22 7F 00 00 00 09 09 00 23 7F 00 00 00 09 09 00 24 7F 00 00 00 09 09 00 25 7F 00 00 00 09 09 00 26 7F 00 00 00 09 09 00 27 7F 00 00 00 0B 0B 00 10 40 00 00 00 0B 0B 00 11 40 00 00 00 0B 0B 00 12 40 00 00 00 0B 0B 00 13 40 00 00 00 0B 0B 00 14 40 00 00 00 0B 0B 00 15 40 00 00 00 0B 0B 00 16 40 00 00 00 0B 0B 00 17 40 00 00 00 F7\n"
constructNameString = constructNameString + "F7\n"
let text = introLine + constructSendString + constructNameString + outroLines
let multipleStringsArray = text.split("\n")
let send = true
for (let i = 0; i < multipleStringsArray.length; i++) {
let string = multipleStringsArray[i]
let stringArray = string.split(" ").map(x => Number("0x" + x))
if (stringArray.length > 2 && send) {
sf.midi.send({
midiBytes: stringArray,
externalMidiPort: "iCON P1-M V1.07 Port 4"
})
}
}
}
setNames()
Great work!
Your sysex doesnāt work on my P1-Nano, but the fact that you found it on midi made me go take a look for the data again.
Using Wireshark on windows, with the USBPcap extension installed, allows us to snoop on the USB communications at the interface level, and once we filter out all the noise (which there is a lot of) and narrow it down to sysex packets I see what appears to be similar to what you see.
At a minimum the device ID of the P1-Nano is different. The āF0 1D 03 10 07ā at the start of all your sysex data has an 06 for the Nano instead of your 07. Not too surprising I suppose.
The iMap Nano program also groups the sysex strings differently. It looks like about the same amount of total information, but with the Nano the sysex for the display text on the LCD is more logically grouped, with each full page of text (16 buttons) all grouped into one sysex line. (i.e., yours is in 8 sysex groups of 10 softkeys, mine is in 5 groups of 16 softkeys).
The button data in yours is 9 groups of 28 (8 byte chunks) while mine is 5 groups of 40 plus one group of 24.
So far I havenāt gotten the Nano to accept a partial dump of the sysex info. Like you said, it seem to want all of it every time. That would be unfortunate if changing the text on a single button would require transmitting the whole block every time.
I also donāt see anywhere in the sysex to control the name of selected DAW, like Bitwig, Cubase, etc. I see where the sysex (at least for the Nano) refers to them by their numbers (as enumerated in iMap) but Iām guessing the lookup table is in the firmware.
Some work left to do, but Iām excited that you found this stuff!
Thanks for the info, I didnāt know about this one.
Thatās the unfortunate thing about these findings. Itās not the best news, I hope thereāll be a solution so that only useful information has to be sent via SysEx.
I wonder if when you connect an Icon Platform M+ with a DU display unit to a DAW, these are also this kind of data packets that are sent? If so, as we already know how to send useful information only to the DU, perhaps we can find the logic behind it, so that only useful information is sent?..
Some key observations (and limitations) after decoding the whole yesterday:
For anyone else who might wade into this or not be intimately familiar with the details:
I noticed similar āanswerā messages like you mentioned on the P1-Nano, but they are different. They can also be ignored on the Nano, as can the pitch bend messages. The āintroā and āoutroā sysex messages also appear unnecessary on the Nano.
Iām not surprised about not being able to remap what the jog wheel sends. Thatās part of the MCU protocol, and I would imagine that allowing that to change could break MCU compatibility with DAWs and users saying āwhy doesnāt this work anymore?ā The P1-Nano iMap wonāt let me change many of the standard MCU physical control assignments, including the jog wheel, the transport controls, or the mute, solo, rec buttons.
Yeah, I had that myself. Mine actually seemed to come from buffering problems on the PC side rather than the Nano, or at least some strange interaction between them. My program for sending (Midi-Ox) defaults to 256 byte buffers, and with sysexās that would run past 256 bytes thatās where the garbage would show up. The bottom right few soft-buttons on mine.
Great write up.
Nice we donāt need the the intro outro message, didnāt think of that. Wonder what that does, maybe some general setting on the device.
Forgot to response regarding the DAW name, indeed thereās no way to edit that as of now it seems. Itās within the device it sets it, based on the sysex that was send. Itās set on the 7th byte. For example:
Iām able to change the message of all keys as well as jog wheel function except the combination of Focus+Jog Wheel, as it seems the Icon device is sending some mouse message outside the MIDI protocol.
Also figured out that you can set different midi message for each of the encoders, depending on what Function Layer is selected, at least on the P1-M, as I have to set those controllers 4 times at the end of setting up the device.
Indeed the display for the Nano does not seem to have the track color strip. Kinda weird, as itās just a color display, and they have the channel metering on there instead. It could easily have been there I thinkā¦
Below is my āfinalā javascript code structure, which has some more clear decoding, if you understand how to read javascript. I can now define two variables, one for the lcd display and one for the device in general. Iāve been able to port it to lua via ChatGPT and have started implementing it in my mixer setup in Reaper.
One thing I could dream for, was that Icon would open up the device for further editing, as the display could easily show more characters etc. Sort of an open sandbox mode, that is not trying to fit in to the MCU box. But that would probably require a lot more as the on processor device has a lot of predefined stuff, such as the DAW name, how the master knob function etc.
But for now this has been better than what I had hoped to find, and already improving my setup. Cheers.
function decimalToHex(decimalNumber, padding = 2) {
let hex = decimalNumber.toString(16).toUpperCase();
while (hex.length < padding) {
hex = '0' + hex;
}
return hex;
}
function centerText(text, maxAmount = 8) {
let spaceAmount = maxAmount - text.length
let newText = text
for (let i = 0; i < spaceAmount; i++) {
if (i < Math.floor(spaceAmount / 2)) {
newText = " " + newText
}
else {
newText = newText + " "
}
}
return newText
}
function getCenterSpace(str) {
let indices = []
for (let i = 0; i < str.length; i++) {
if (str[i] === ' ') {
indices.push(i);
}
}
if (indices.length == 1 && indices[0] <= 8 && str.length - (indices[0] + 1) <= 8) {
return indices[0]
}
else if (indices.length > 1) {
return -2
}
else {
return -1
}
}
function textToHex(text) {
let hexValues = [];
let indexValue;
let fullText;
let firstPart;
let secondPart;
text = text.trim() // remove space before or after text
if (text.length < 8) {
indexValue = text.length % 2 === 0 ? "03" : "01";
fullText = centerText(text) + centerText("")
firstPart = centerText(text)
secondPart = centerText("")
}
else if (text.length == 8) {
indexValue = "02";
fullText = text + centerText("")
} else {
let spaceIndex = getCenterSpace(text)
if (spaceIndex == -1) {
firstPart = text.slice(0, 8);
secondPart = text.slice(8);
fullText = centerText(firstPart) + centerText(" " + secondPart)
}
else if (spaceIndex == -2) {
firstPart = text.slice(0, 8);
secondPart = (text.slice(8) + centerText("")).slice(0, 8);
fullText = centerText(firstPart) + centerText(secondPart)
}
else {
firstPart = text.slice(0, spaceIndex);
secondPart = text.slice(spaceIndex + 1);
fullText = centerText(firstPart) + centerText(secondPart)
}
// 04 both uneven
if (firstPart.length % 2 == 1 && secondPart.length % 2 == 1) {
indexValue = "04";
}
// 05 center top row
else if (firstPart.length % 2 == 1 && secondPart.length % 2 == 0) {
indexValue = "05";
}
// 06 center low row
else if (firstPart.length % 2 == 0 && secondPart.length % 2 == 1) {
indexValue = "06";
}
// 07 center both rows
else if (firstPart.length % 2 == 0 && secondPart.length % 2 == 0) {
indexValue = "07";
}
firstPart = centerText(firstPart)
secondPart = centerText(secondPart)
}
// Convert the text to an array of hex values
let hexText = indexValue + " " + fullText.split('').map(char => char.charCodeAt(0).toString(16).toUpperCase()).join(' ');
return hexText;
}
function setNames() {
// 06 is for reaper on port 1. 26 would be port 2 and 46 would be port 3
function openSysexMapping(line) {
return "F0 1D 03 10 07 25 06 " + decimalToHex(line) + " 1C"
}
function openSysexName(line) {
return "F0 1D 03 10 07 26 06 " + decimalToHex(line) + " 0A"
}
/*
28, 29, 2A, 2B, 2C, 2D,
33, 34, 35, 36, 37, 38, // used
39, 3A, 3B,
3D, 3E, 3F, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 51, 57,
58, 59, 5A, 70, 71, 72,
73, 74, 75, 76, 77, 78, 79, 7A, 7B, 7C, 7D, 7E, 7F
*/
function createSysexString(info) {
if (typeof info === "string") {
return info
}
else {
let pattern = info.pattern ? info.pattern : "00"
return [info.type, info.type, info.channel, info.number, info.value, "00","02",pattern].join(" ")
}
}
const emptyMapping = { name: "", number: "00", type: "00", value: "7F", channel: "00" }
let randomNumber = Math.floor(Math.random() * 9)
// for encoders pattern 00 is incremental and 10 is numerical
const lcdSettings = {
1: { name: "Volume", number: "33", type: "09", value: "7F", channel: "00" },
2: { name: "Pan", number: "34", type: "09", value: "7F", channel: "00" },
3: { name: "Pan Width", number: "35", type: "09", value: "7F", channel: "00" },
5: { name: "Stereo Balance", number: "37", type: "09", value: "7F", channel: "00" },
6: { name: "Stereo Pan", number: "38", type: "09", value: "7F", channel: "00" },
7: { name: "Dual Pan", number: "39", type: "09", value: "7F", channel: "00" },
14: { name: Date.now() + "", number: "51", type: "09", value: "7F", channel: "00" },
15: { name: randomNumber + "", number: "51", type: "0B", value: "7F", channel: "0" + randomNumber },
}
const deviceSettings = {
// PLATFORM SETTINGS
leftButton: { number: "30", type: "09", value: "7F", channel: "00" }, // 48
rightButton: { number: "31", type: "09", value: "7F", channel: "00" }, // 49
bankLeftButton: { number: "2E", type: "09", value: "7F", channel: "00" }, // 46
bankRightButton: { number: "2F", type: "09", value: "7F", channel: "00" }, // 47
flipButton: { number: "32", type: "09", value: "7F", channel: "00" }, // 50
lockButton: { number: "00", type: "00", value: "7F", channel: "00" },
offButton: { number: "4F", type: "09", value: "7F", channel: "00" }, // 73
readButton: { number: "4A", type: "09", value: "7F", channel: "00" }, // 74
writeButton: { number: "4B", type: "09", value: "7F", channel: "00" }, // 75
trimButton: { number: "4C", type: "09", value: "7F", channel: "00" }, // 76
latchButton: { number: "4E", type: "09", value: "7F", channel: "00" }, // 78
touchButton: { number: "4D", type: "09", value: "7F", channel: "00" }, // 77
rewindButton: { number: "5B", type: "09", value: "7F", channel: "00" }, // 91
forwardButton: { number: "5C", type: "09", value: "7F", channel: "00" }, // 92
loopButton: { number: "56", type: "09", value: "7F", channel: "00" }, // 86
stopButton: { number: "5D", type: "09", value: "7F", channel: "00" }, // 93
playButton: { number: "5E", type: "09", value: "7F", channel: "00" }, // 94
recordButton: { number: "5F", type: "09", value: "7F", channel: "00" }, // 95
armButton1: { number: "00", type: "09", value: "7F", channel: "00" }, // 0
armButton2: { number: "01", type: "09", value: "7F", channel: "00" }, // 1
armButton3: { number: "02", type: "09", value: "7F", channel: "00" }, // 2
armButton4: { number: "03", type: "09", value: "7F", channel: "00" }, // 3
armButton5: { number: "04", type: "09", value: "7F", channel: "00" }, // 4
armButton6: { number: "05", type: "09", value: "7F", channel: "00" }, // 5
armButton7: { number: "06", type: "09", value: "7F", channel: "00" }, // 6
armButton8: { number: "07", type: "09", value: "7F", channel: "00" }, // 7
pushKnob1: { number: "20", type: "09", value: "7F", channel: "00" }, // 32
pushKnob2: { number: "21", type: "09", value: "7F", channel: "00" }, // 33
pushKnob3: { number: "22", type: "09", value: "7F", channel: "00" }, // 34
pushKnob4: { number: "23", type: "09", value: "7F", channel: "00" }, // 35
pushKnob5: { number: "24", type: "09", value: "7F", channel: "00" }, // 36
pushKnob6: { number: "25", type: "09", value: "7F", channel: "00" }, // 37
pushKnob7: { number: "26", type: "09", value: "7F", channel: "00" }, // 38
pushKnob8: { number: "27", type: "09", value: "7F", channel: "00" }, // 39
pushJog: { number: "65", type: "09", value: "7F", channel: "00" }, // 101
encoderKnob1: { number: "10", type: "0B", value: "40", channel: "00" }, // 16
encoderKnob2: { number: "11", type: "0B", value: "40", channel: "00" }, // 17
encoderKnob3: { number: "12", type: "0B", value: "40", channel: "00" }, // 18
encoderKnob4: { number: "13", type: "0B", value: "40", channel: "00" }, // 19
encoderKnob5: { number: "14", type: "0B", value: "40", channel: "00" }, // 20
encoderKnob6: { number: "15", type: "0B", value: "40", channel: "00" }, // 21
encoderKnob7: { number: "16", type: "0B", value: "40", channel: "00" }, // 22
encoderKnob8: { number: "17", type: "0B", value: "40", channel: "00" }, // 23
jogWheel: { number: "3C", type: "0B", value: "40", channel: "00" }, // 60
unknown1: { number: "66", type: "09", value: "7F", channel: "00" },
unknown2: { number: "67", type: "09", value: "7F", channel: "00" },
touchFader1: { number: "68", type: "09", value: "7F", channel: "00" }, // 104
touchFader2: { number: "69", type: "09", value: "7F", channel: "00" }, // 105
touchFader3: { number: "6A", type: "09", value: "7F", channel: "00" }, // 106
touchFader4: { number: "6B", type: "09", value: "7F", channel: "00" }, // 107
touchFader5: { number: "6C", type: "09", value: "7F", channel: "00" }, // 108
touchFader6: { number: "6D", type: "09", value: "7F", channel: "00" }, // 109
touchFader7: { number: "6E", type: "09", value: "7F", channel: "00" }, // 110
touchFader8: { number: "6F", type: "09", value: "7F", channel: "00" }, // 111
touchFader9: { number: "70", type: "09", value: "7F", channel: "00" }, // 112
fader1: { number: "00", type: "0E", value: "7F", channel: "00" }, //
fader2: { number: "00", type: "0E", value: "7F", channel: "01" }, //
fader3: { number: "00", type: "0E", value: "7F", channel: "02" }, //
fader4: { number: "00", type: "0E", value: "7F", channel: "03" }, //
fader5: { number: "00", type: "0E", value: "7F", channel: "04" }, //
fader6: { number: "00", type: "0E", value: "7F", channel: "05" }, //
fader7: { number: "00", type: "0E", value: "7F", channel: "06" }, //
fader8: { number: "00", type: "0E", value: "7F", channel: "07" }, //
fader9: { number: "00", type: "0E", value: "7F", channel: "08" }, //
jogMoveHorLeft: { number: "62", type: "09", value: "7F", channel: "00" },
jogMoveHorRight: { number: "63", type: "09", value: "7F", channel: "00" },
jogMoveVerLeft: { number: "60", type: "09", value: "7F", channel: "00" },
jogMoveVerRight: { number: "61", type: "09", value: "7F", channel: "00" },
navigationLeft: { number: "62", type: "09", value: "7F", channel: "00" },//"40 05 00 00 00 00 00 00", // sends arrows
navigationRight: { number: "62", type: "09", value: "7F", channel: "00" },//"40 04 0F 00 00 00 00 00", // sends arrows
focusLeft: "50 00 00 00 02 00 00 00",
focusRight: "50 00 00 00 01 00 00 00",
jogZoomVerLeft: { number: "64", type: "09", value: "7F", channel: "00" }, // 82
jogZoomVerRight: { number: "65", type: "09", value: "7F", channel: "00" }, // 83
jogZoomHorLeft: { number: "66", type: "09", value: "7F", channel: "00" }, // 84
jogZoomHorRight: { number: "67", type: "09", value: "7F", channel: "00" }, // 85
masterButton: { number: "00", type: "00", value: "7F", channel: "00" },
shuffleButton: { number: "00", type: "00", value: "7F", channel: "00" },
moveVerButton: { number: "00", type: "00", value: "7F", channel: "00" },
moveHorButton: { number: "00", type: "00", value: "7F", channel: "00" },
navigationButton: { number: "00", type: "00", value: "7F", channel: "00" },
focusButton: { number: "00", type: "00", value: "7F", channel: "00" },
zoomHorButton: { number: "00", type: "00", value: "7F", channel: "00" },
zoomVerButton: { number: "00", type: "00", value: "7F", channel: "00" },
soloButton1: { number: "08", type: "09", value: "7F", channel: "00" }, // 8
soloButton2: { number: "09", type: "09", value: "7F", channel: "00" }, // 9
soloButton3: { number: "0A", type: "09", value: "7F", channel: "00" }, // 10
soloButton4: { number: "0B", type: "09", value: "7F", channel: "00" }, // 11
soloButton5: { number: "0C", type: "09", value: "7F", channel: "00" }, // 12
soloButton6: { number: "0D", type: "09", value: "7F", channel: "00" }, // 13
soloButton7: { number: "0E", type: "09", value: "7F", channel: "00" }, // 14
soloButton8: { number: "0F", type: "09", value: "7F", channel: "00" }, // 15
muteButton1: { number: "10", type: "09", value: "7F", channel: "00" }, // 16
muteButton2: { number: "11", type: "09", value: "7F", channel: "00" }, // 17
muteButton3: { number: "12", type: "09", value: "7F", channel: "00" }, // 18
muteButton4: { number: "13", type: "09", value: "7F", channel: "00" }, // 19
muteButton5: { number: "14", type: "09", value: "7F", channel: "00" }, // 20
muteButton6: { number: "15", type: "09", value: "7F", channel: "00" }, // 21
muteButton7: { number: "16", type: "09", value: "7F", channel: "00" }, // 22
muteButton8: { number: "17", type: "09", value: "7F", channel: "00" }, // 23
selButton1: { number: "18", type: "09", value: "7F", channel: "00" }, // 24
selButton2: { number: "19", type: "09", value: "7F", channel: "00" }, // 25
selButton3: { number: "1A", type: "09", value: "7F", channel: "00" }, // 26
selButton4: { number: "1B", type: "09", value: "7F", channel: "00" }, // 27
selButton5: { number: "1C", type: "09", value: "7F", channel: "00" }, // 28
selButton6: { number: "1D", type: "09", value: "7F", channel: "00" }, // 29
selButton7: { number: "1E", type: "09", value: "7F", channel: "00" }, // 30
selButton8: { number: "1F", type: "09", value: "7F", channel: "00" } // 31
};
let keyOrder = ["leftButton", "rightButton", "bankLeftButton", "bankRightButton", "flipButton", "lockButton", "offButton", "readButton", "writeButton", "trimButton", "latchButton", "touchButton", "rewindButton", "forwardButton", "loopButton", "stopButton", "playButton", "recordButton", "armButton1", "armButton2", "armButton3", "armButton4", "armButton5", "armButton6", "armButton7", "armButton8", "pushKnob1", "pushKnob2", "pushKnob3", "pushKnob4", "pushKnob5", "pushKnob6", "pushKnob7", "pushKnob8", "pushJog", "encoderKnob1", "encoderKnob2", "encoderKnob3", "encoderKnob4", "encoderKnob5", "encoderKnob6", "encoderKnob7", "encoderKnob8", "jogWheel", "unknown1", "unknown2", "touchFader1", "touchFader2", "touchFader3", "touchFader4", "touchFader5", "touchFader6", "touchFader7", "touchFader8", "touchFader9", "fader1", "fader2", "fader3", "fader4", "fader5", "fader6", "fader7", "fader8", "fader9", "jogMoveHorLeft", "jogMoveHorRight", "jogMoveVerLeft", "jogMoveVerRight", "navigationLeft", "navigationRight", "focusLeft", "focusRight", "jogZoomVerLeft", "jogZoomVerRight", "jogZoomHorLeft", "jogZoomHorRight", "masterButton", "shuffleButton", "moveVerButton", "moveHorButton", "navigationButton", "focusButton", "zoomHorButton", "zoomVerButton", "soloButton1", "soloButton2", "soloButton3", "soloButton4", "soloButton5", "soloButton6", "soloButton7", "soloButton8", "muteButton1", "muteButton2", "muteButton3", "muteButton4", "muteButton5", "muteButton6", "muteButton7", "muteButton8", "selButton1", "selButton2", "selButton3", "selButton4", "selButton5", "selButton6", "selButton7", "selButton8"]
let sysexTextArray = []//["F0 1D 03 10 07 21 0E 58 32 6D 65 58 12 7A 03 F7\n"]
let pushMapping = true
let sysexLine = 1
let mappingsAdded = 0
if (pushMapping) {
for (let i = 1; i <= 80; i++) {
if (mappingsAdded % 28 == 0) {
sysexTextArray.push(openSysexMapping(sysexLine))
sysexLine++
}
if (lcdSettings[i]) {
sysexTextArray.push(createSysexString(lcdSettings[i]))
}
else {
sysexTextArray.push(createSysexString(emptyMapping))
}
if (mappingsAdded % 28 == 27) {
sysexTextArray.push("F7\n")
}
mappingsAdded++
}
for (let k = 0; k < keyOrder.length; k++) {
let key = keyOrder[k]
if (mappingsAdded % 28 == 0) {
sysexTextArray.push(openSysexMapping(sysexLine))
sysexLine++
}
sysexTextArray.push(createSysexString(deviceSettings[key]))
if (mappingsAdded % 28 == 27) {
sysexTextArray.push("F7\n")
}
mappingsAdded++
}
// we add the encoder push knob buttons 4 more times for each of the function layers
keyOrder = ["pushKnob1", "pushKnob2", "pushKnob3", "pushKnob4", "pushKnob5", "pushKnob6", "pushKnob7", "pushKnob8", "encoderKnob1", "encoderKnob2", "encoderKnob3", "encoderKnob4", "encoderKnob5", "encoderKnob6", "encoderKnob7", "encoderKnob8"]
for (let p = 0; p < 4; p++) {
for (let k = 0; k < keyOrder.length; k++) {
let key = keyOrder[k]
let modifiedSysex = { number: deviceSettings[key].number, type: deviceSettings[key].type, value: deviceSettings[key].value, channel: decimalToHex(p+1) }
if (mappingsAdded % 28 == 0) {
sysexTextArray.push(openSysexMapping(sysexLine))
sysexLine++
}
sysexTextArray.push(createSysexString(modifiedSysex))
if (mappingsAdded % 28 == 27) {
sysexTextArray.push("F7\n")
}
mappingsAdded++
}
}
}
sysexLine = 1
mappingsAdded = 0
for (let i = 1; i <= 80; i++) {
if (mappingsAdded % 10 == 0) {
sysexTextArray.push(openSysexName(sysexLine))
sysexLine++
}
if (lcdSettings[i]) {
sysexTextArray.push(textToHex(lcdSettings[i].name))
}
else {
sysexTextArray.push(textToHex(emptyMapping.name))
}
if (mappingsAdded % 10 == 9) {
sysexTextArray.push("F7\n")
}
mappingsAdded++
}
//sysexTextArray.push("EF 7F 7F\n")
//sysexTextArray.push("EC 22 06\n")
//sysexTextArray.push("F0 1D 03 10 07 24 0E 58 32 6D 65 58 12 7A 03 F7")
let fullSysexText = sysexTextArray.join(' ').replace(/\n /g, '\n')
sf.clipboard.setText({ text: sysexTextArray.join(' ').replace(/\n /g, '\n') })
let multipleStringsArray = fullSysexText.split("\n")
let send = true
for (let i = 0; i < multipleStringsArray.length; i++) {
let string = multipleStringsArray[i]
let stringArray = string.split(" ").map(x => Number("0x" + x))
if (stringArray.length > 2 && send) {
sf.midi.send({
midiBytes: stringArray,
externalMidiPort: "iCON P1-M V1.07 Port 4"
})
sf.wait({ intervalMs: 30 })
}
}
}
setNames()
Thatās fantastic. I started mapping where all the various controls are in that sysex stream as well. Iāll compare your P1-M to my P1-Nano and see how different they are.
One quick question - I donāt see anything in your code above (on first glance) for controlling the color bars or the bottom parts of the scribble strips. I know those donāt run on the āport 4ā with all this iMap stuff, but do you happen to have that detail handy somewhere?