Roland MKS-80 M4L device: questions, critique, suggestion

Markus Schlösser's icon

Hey everybody,
This my first device which I started from scratch, in the past I've been mostly messing with other m4l devices or have reworked stuff (recently did a device for the Eowave Magma, which is a Roland 101 clone but with enhanced capabilities like 3 ENVs and 2 LFOs, only CC stuff).
I have MKS-80 rev5 and came quite far with my what I have done. Mainly because the mks has a very easy sysex implementation.

My questions:

1. VCO-2 RANGE ........... 0 = Low Frequency
36 - 84 (60=middle C of 8')
100 = High Frequency
How would I deal with this? For VCO1Range I just have a live.dial with an enum ("32C 32C# 32D 32D# 32E 32F 32F# 32G 32G# 32A 32A# 32B 16C 16C# ......), and a +36 which works fine. But I don't know how to get the same notes as above but with the 0 for LOW and 100 for high?

2. I know the patches may be messy for most, but with all those lines it makes it easier for me to debug things. Is there anything you see in my device which seems wrong to you? Anything that could be done better or more efficient?
3. The mks-80 sends it's current preset data as sysex when receiving a program change, so my next step (besides tidying up and making it look more nicely) would be to try to gather, parse and then feed that data into the dials etc to have the correct representation of the patch visually shown. I saw lots of older hints about this here in the forum but is there anything new from the last couple of years which I should be using for this? How would you approach this today?
One example from Midi-OX of what the MKS sends after having received a prg change:
16179465 2- -- 240 Buffer: 74 Bytes System Exclusive
SYSX: 240 0 33 69 127 0 115 101 110 100 77 101 115 115 97 103 101 58
SYSX: 32 112 111 114 116 61 48 44 32 99 104 97 110 110 101 108 61 49
SYSX: 44 32 116 121 112 101 61 112 114 111 103 114 97 109 67 104 97 110
SYSX: 103 101 44 32 112 114 111 103 114 97 109 78 117 109 98 101 114 61
SYSX: 49 247
or in hex:
SYSX: F0 00 21 45 7F 00 73 65 6E 64 4D 65 73 73 61 67 65 3A
SYSX: 20 70 6F 72 74 3D 30 2C 20 63 68 61 6E 6E 65 6C 3D 31
SYSX: 2C 20 74 79 70 65 3D 70 72 6F 67 72 61 6D 43 68 61 6E
SYSX: 67 65 2C 20 70 72 6F 67 72 61 6D 4E 75 6D 62 65 72 3D
SYSX: 31 F7
Strangely enough the sysex specs imho states differently, under 3.3. it says
"When the 'Patch Number' is changed while the MIDI FUNCTION is set at III, the following exclusive messages ( A through E ) are sent in sequence." which doesn't seem to be the case. Instead the "F0 00 .." really confuses me, as this sequence is not found in the spec.
Does anybody know what's going on here?

Thanks in advance
Markus

Max Patch
Copy patch and select New From Clipboard in Max.

MKS80syx.txt
text/plain 38.91 KB
MKS-80 Sysex Specification


redhexagonal's icon

I made An MKS50 editor which worked OK, take a look, probably similar. https://maxforlive.com/library/device/3534/jl-mks-50-control
You’ll have to edit it to send and recieve sysex though, the imp.midi object is obsolete now. Live does sysex now anyway.
Sysex is a pain in the ass tbh. There are so many ways data is packed and most documentation is photocopied and often has errors. If you’re just starting with Max its not A good place to start because it will be hard to tell if your max patch is built wrong or your packing/unpacking method or original documentation is wrong…

Markus Schlösser's icon

cleaned up presentation mode and parameters

Max Patch
Copy patch and select New From Clipboard in Max.

Markus Schlösser's icon

I have studied your device(s) intensely :-) So thanks for that and also for the reply!
Wanna get around to implementing the ENV view as well later as it's so much more visual.
I love a challenge ;-) Last year I did a Mackie C4 midi remote script (of course together with somebody else), and have worked with sysex in the past, but thanks for the warning.
Right now I am nearly done with the first part of just sending stuff to the MKS-80, and it works beautifully 🤩💪
RE packing/unpacking: I have a couple of tools handy which will help me, namely the Electra One with it's excellent sysex parser web frontend, also it has an existing preset for the mks80, which made getting params and values into Midi-Ox very easy. Twist knob, look at MidiOX in decimal mode and then basically copy and paste to max. The other tool is knobkraft, a great librarian which makes keeping note of what's going in and out of the mks a lot easier.
I might bother you again w re to questions around your patches 😇

Markus Schlösser's icon

Update:
1. Point 3 of the original question is moot, what I saw came from the Electra ONE and is therefore not relevant.
2. I have found a couple of old tools, some of them even with the source code, that helped me immensely in understanding what's going on and the lingo that Roland used. So for example the "request program buffer" is named by Roland as "Request a file (RQF)", this is:
Hex Byte Description
----- ---------- ----------------------------------------
a F0 1111 0000 Exclusive Status
b 41 0100 0001 Roland ID #
c 41 0100 0001 Operation code
d '0n' 0000 nnnn Unit # : MIDI basic channel, nnnn=0 - 15
where nnnn = 0 for channel 1
e 20 0010 0000 Format type
f 4D 0100 1101 M :
4B 0100 1011 K :
53 0101 0011 S : - File Name in ASCII
2D 0010 1101 - :
38 0011 1000 8 :
30 0011 0000 0 :
g 'cs' 0000 0000 Check Sum
h F7 1111 0111 End of System Exclusive

So in my case:
HEX: F0 41 41 00 20 4D 4B 53 2D 38 30 00 F7
DEC: 240 65 65 0 32 77 75 83 45 56 48 0 247

Sending that to the MKS-80, the mks replies with what the manual describes as "5.1.3 Data (DAT)", which is 16 messages.
From the manual: "Each DAT message consists of 4 sets of the Patch and Tone data. Each data set consists of 62 bytes total --- 39 bytes for Tone Parameters of a number and 23 bytes for Patch Parameters of the same number. These parameters are sent in sequence of the 'Tone Number's and 'Patch Number's. 2 DATs are sent for each 'Bank'. In normal operation, 16 DATs are totally sent for all 'bank's (1 - 8)."
Transfer end is then indicated by SYSX: F0 41 45 00 20 F7 (240 65 69 0 32 247)
Questions:
1. I assume this is ONE patch? And not all of the ones in the current bank? (not sure)
2. After the intro (F0 41 42 00 20) it continues with 1C (28). I assume all the following are just the actual values of the params?
3. So after sending a program change, I would then request that program number and parse it. Probably with a bit of delay? How much?
4. Does that I mean I can just ROUTE those single values to its respective live.dials etc? This is what I did in the past with ctlin for normal CC synths and it worked fine. How would I approach this in the most efficient way?
5. Updated device here:

Max Patch
Copy patch and select New From Clipboard in Max.

and it looks like this:


Markus Schlösser's icon

Published at https://maxforlive.com/library/device.php?id=8106

Markus Schlösser's icon

the question that still remains and I can't come up with a nice solution is:
1. VCO-2 RANGE:
0 = Low Frequency
36 - 84 (60=middle C of 8')
100 = High Frequency

How would I deal with this? For VCO1Range I just have a live.dial with an enum ("32C 32C# 32D 32D# 32E 32F 32F# 32G 32G# 32A 32A# 32B 16C 16C# ......), and a +36 which works fine. But I don't know how to get the same notes as above but with the 0 for LOW and 100 for high?

Markus Schlösser's icon

ok, so first I now want to implement the PER PATCH parsing and I managed to get the the patch data from the mks80 (when I select a preset ON the synth, it sends out 5 messages:
1. A. PGR (Program Number) which indicates the 'Patch Number'
2. B. APR (All Parameter) which indicates the Patch Parameters for Upper Section
3. C. APR (All Parameter) which indicates the Patch Parameters for Lower Section
4. D. APR (All Parameter) which indicates the Tone Parameters for Upper Section
5. E. APR (All Parameter) which indicates the Tone Parameters for Lower Section
I used

from there I go to "unpack" and managed to get the parameter for VCF Cutoff Frequency into the existing live.dial and it updates just fine :-)

Problem: (tldr) How to deal with upper/lower?
Long explanation: For every parameter I have a live.dial, live.toggle or live.menu
From there I go to a sxformat, in the case of low pass cutoff sxformat 240 65 54 0 32 32 / is $i2 / 24 / is $i1 / 247

$i1 is the the value (0-100) to be sent for parameter 24 (cutoff),
$i2 is the indicator for upper or lower (1 or 2), which is signaled by a live.tab, sending out 1 or 2 for $i2

this all goes to a midiout for every parameter.
Simplified patch picture solely for cutoff:


So far so good. But how do I apply the upper/lower logic for parsing?
I would like to update the live.dial for the currently selected (upper OR lower) tone. PLUS when I switch from upper to lower, I want to of course have the value which was previously sent, but not yet shown.
I assume, I need some kind of intermediate storage? I can deal with the basics in m4l and this patch has been working fine for the last year or so (and includes 65 or so parameters), but this is beyond me current knowledge. Any help is highly appreciated! :-)

Source Audio's icon

if you receive sysex dump, and stuff it into coll,
then you allready have data stored.
It is only a matter to organise coll to be able to recall
parts of the data string, to again populate that dials and whatever with received values.

if you post full sysex string in decimal form
placing each parameter into own line, you will get the idea.

and why 1 dial with high low switch, instead of 2 dials,
one for low, the other for high ?
would make more sense and solve your problem - not knowing how to store - recall dial value depending on switch state.

Markus Schlösser's icon

Thanks for the reply! I do appreciate it :-)

I will need to learn more about coll, but am I able to recall messages? Because the mks80 sends 5 messages per preset:
11 Bytes System Exclusive

240 65 52 0 32 48 0 0 1 0 247

(PGR (Program Number) which indicates the 'Patch Number')

23 Bytes System Exclusive

240 65 53 0 32 48 1 0 60 50 1 2 2 0 2 0 18 1 1 0 1 55 247

(APR (All Parameter) which indicates the Patch Parameters (15) for UPPER Section), the 1 before the bold 0 60 indicates if upper (1) or lower (2)

23 Bytes System Exclusive

240 65 53 0 32 48 2 0 60 50 1 2 2 0 0 39 18 1 1 50 0 58 247

(APR (All Parameter) which indicates the Patch Parameters (15) for LOWER Section)

56 Bytes System Exclusive

240 65 53 0 32 32 1 64 1 3 0 0 0 51 1 0 0 1 0 0 0 2 60 2 1 0 60 65 2 51 37 47 0 0 1 32 0 97 98 0 100 97 1 0 0 48 48 18 7 1 57 41 100 64 84 247

(APR (All Parameter) which indicates the TONE Parameters (48) for UPPER Section), the 1 before the bold 64 1 3 indicates if upper (1) or lower (2)

56 Bytes System Exclusive

240 65 53 0 32 32 2 64 1 3 0 0 0 51 1 0 0 1 0 0 0 2 60 2 1 0 60 65 2 51 37 47 0 0 1 32 0 97 98 0 100 97 1 0 0 48 48 18 7 1 57 41 100 64 84 247
(APR (All Parameter) which indicates the TONE Parameters (48) for LOWER Section)

The BOLD parts are the relevant ones. So to update all my GUI dials etc I will need to parse/unpack all the bold numbers separately for upper and lower. So for example, the first 97 represents the VCF cutoff value. I took inspiration from the nice jx8p m4l device, which just uses unpack, but as the jx8p doesn't have the upper/lower concept, I couldn't find inspiration for that :-). Would you still recommend using unpack? (unpack is easy for me to grasp the concept of, anything else I will need to learn, which is fine, but could take longer)


For the second question, only 1 dial for 2 reasons:
1. the mks80 and all controllers have the same concept
2. I already have 63 (15 bytes + 48 bytes) parameters in the form of dial, toggles and menus per layer, and doubling those would make the device even bigger than it already is (see 4 posts up).

I don't necessarily need a proper long-term storage or patch management, because I manage presets for most of my synths with the fantastic open source software "Knobkraft".

Source Audio's icon

you can place your parameter sections into coll easily
by using sysex message headers as indexes.
for example
240 65 52 0 32 48 0 0 1 0 247
you know that this one contains preset number, could be stored in coll as
0, 0 0 1 0; ( or whatever indicates preset number out of 4 ints)

similar to 4 remaining strings, index them as

now you know that for example index 3 is upper parameter string with 48 values.
to grab parameter number 12, send message nth 3 12 to coll .
coll sends 12th item in index line 3 out.

or you keep lists as shown where dials are, and when you switch
parameter 12 to high, grab 12th value in the high parameter list.
can be done with zl objects, value etc.

That all depends on your design, where the dials and switches are .
-----
But first thing you need is to separate sysex messages.
If dump allways comes in in exact order, and you ignore 240 & 247 ,
and split complete dump list using zl slice and zl ecils.
here is simulated dump, using your lists

having that, it is easy to place sections into coll, or value objects and
grab values remotely.

P.S. if you don't need upper/lower switch for each dial separately,
things are much simpler.
you recall the whole string and unpack elements.


Markus Schlösser's icon

thank you (again) for that extensive reply and examples! Will need to read that at least 10 more times to fully grasp it and what to do with it. The whole trigger zl slice ecils part is unfortunately a bit over my head currently.
I have ONE upper/lower, which goes to the second inlet of every sxformat, but not the live.dials/toggles:

(I know, I should have probably used send/receive, but this was my very first m4l device and it works 😇)
You also see the first try of parsing using unpack in the upper right corner.

My first thought was just using the upper/lower toggle to recall from coll using nth, which would than update all dials etc.

So can't I "just" use to 2 unpacks, one for the 15 bytes, another for the 48 bytes, hardwire them to the corresponding live.dials, toggles etc, and when I switch upper/lower it would then also bang coll with the appropriate upper/lower index? (something like "if UPPER, than index 2 AND 4, elif LOWER than index 3 AND 5). Or there a logical error in this? Can the nth position argument be a range?
Or would it be easier to first join the corresponding upper and lower messages and then only use 1 unpack? (or is that exactly what you are doing in your example? 🤔)

I would also need to send the first message (program number) to the live.dial that handles program change, so in pseudocode something like "if message length = 11 bytes, than send the 9th entry to the patch live.dial (hoping that this doesn't trigger it?).

UPDATE: thinking about it further, that approach would not cover the initial handling of the five messages, because I have to send the results of unpack depending on the current state of the upper/lower live.toggle 🤔

Source Audio's icon

Sorry, but I don't really understand what your goal with this is.

to receive dump and store values is one thing.
But what do you want to do with them ?
If you want to populate all dials with values, which ones ?
upper or lower ?
you said that dump sends both upper and lower lists, are they both part of the same sound
or are this 2 sounds which one recalls ?
I know nothing about that synth.

I suppose it is one switch that serves ALL dials.
That one should recall 15 + 48 values, upper or lower, and it should only set
dial values, without sending them out to synth, because it is allready having that values set.
Now you have dials set and tweak manually some parameters, sending them to synth.
Then you switch to lower and pull that from received dump.
then you switch back to upper, and then what ? pull again from the dump, which would override your previous changes, and if you don't want that, you must update stored values in the coll.

I would suggest, build your logic in minimised form, using only short lists and see how to cope with that.

maybe decide that whenever dump gets received, lower part gets sent to dials,
by setting switch to that position at dump end.

Markus Schlösser's icon

That whole upper and lower tone concept by Roland has confused users and especially developers for over 40 years, especially when it comes to the user experience aspects of it. Basically what it boils down to is that one patch always consists of Two Tones (upper and lower) and these can either be used for split mode, dual mode or layer mode, for example for extra wide stereo images. You can think of it as something similar to a multimode.

Currently the device is unidirectional - only goes from Live to the synthesizer. I want to make it bi-directional and for that I have to parse the incoming sysex and then update the Dials which previously have only send values out and now they also have to update according to what's coming in.
So yes, I want to tweak manually and automate based on the previously received values.

Good thinking about the storing part! I would have forgotten that, doesn't make it easier though.
Also very good idea to just switch to upper when receiving (upper is the default).

So how would go about when receiving the 5 messages, to take the first and update the program live.dial, take the second and forth to update the Dials and switch the upper/lower toggle to upper?
Also when switching that toggle to other value, write all current values to coll and update the existing values in coll?
Immensely grateful for your help!

UPDATE: I thought the least I could do when you help me, is clean up all the cables, so here it is:

Would it help, if I posted the patch here?

Source Audio's icon

Thanks for explantion about that dual voice thing.
I think least problem is to store and route sysex dump.
What comes next is problem with Live and it's automation,
if you want to store synth presets into Live set, or build
external Preset bank, and so on.
I don't use Live, an have no interest in it,
but want to point that few things out.
if you use 1 single Dial for 2 parameters, how would you automate or store them into Live set ?

That are all unknown details for me, otherwise it would be easy thing
to receive sysex, store it into file, switch between upper & lower etc.
You can see it in the 2 screenshots I posted, coll or split list ...

Maybe best would be to use double set of dials, and show or hide
one or the other, depending on that switch, but have them allways
available for direct automation and storage.

It would be helpfull to have the patch, but I am sure I would immediately
kill presentation mode, pack all objects into subpatchers and so on.

Most helpfull would be if you explain what you want to do with this,
I mean the questions about storing presets, where, and in which form,
naming them ?, having umenu with listed presets in a bank ...
Sending dump back to synth would be much more efficient
in case you load a preset from stored bank, instead of sending individual
sysex strings.
One would use them rather for manual tweaking.

Source Audio's icon

I see that you repeatedly ask how to deal with VCO2 range

Source Audio's icon

"F ENV Polarity" in your device has wrong parameter number.
(real parameter name is VCF ENV POLARITY)
it duplicates PWM Polarity (id 8) instead of correct id 27

Markus Schlösser's icon

1. wow, thank you for checking that wrt "F ENV Polarity", will update later today.
2. Also while I couldn't fall asleep last night, I thought about your VCO2 range proposal and NOW I understand it! :-)
3. wrt Sysex parsing: Doubling the amount of controls is imho not a good solution, because this would also double the amount of parameters on control surfaces and as I already have so many, I think this is not a good solution. I will try to write down different scenarios and flows today or tomorrow, in order to better explain. But one thing I thought about, if I could use a gwitch in front of every dial/toggle, where the actual switching depends on the state of the upper/lower toggle? I'm also currently learning about the different storage options.

Source Audio's icon

I had a look at MKS-80 midi implementation, to be able to talk about it without telling nonesense.
I think you need to read and understand that few infos about
what it does in which midi mode when it receives external messages.
Mode III , which I thing is needed to do what you are trying, will allways response with sysex messages when it receives program change, or even parameter change.
Sort of sysex feedback.
You need to decide how to deal with that.
If you want to receive only dumps of full patch when you send
program change, you need to block input of sysex messages in all other situations, like when you tweak parameters and send them out.
Or better say open sysex capturing gate only when you want,
in best case linked as send PG change as dump request,
because you might want to recall patches without whole sysex carousel.

-------
I am 100% convinced that only proper solution is to have both upper and lower values and their gui elements placed in the patch.
upper/lower switch should only select what gets visible on the GUI.

You might think about it as long as you want, but that will probably be
the result at the end.
and - I would trash that big roland logo an instead make dials and their
info / values text better visible, and also place elements in their parameter order.
do you know this one :
https://roland-mks-80-editor-controlle.jimdofree.com/#link1

for the price of one drink ...

Markus Schlösser's icon

Thanks for looking into that sysex implementation! I do understand the crazy midi modes that it has (having tested it a lot last year) :-) and yes, basically Mode III is the only possible scenario (with the exception of that "Tone" change only works in Mode II).

"will allways response with sysex messages when it receives program change, or even parameter change. Sort of sysex feedback. You need to decide how to deal with that."
The problem is, that the MKS80 will NOT reply sysex when doing program change from externally. It WILL however send a sysex reply for the selected patch when manually selecting a patch ON the synth (that is what I posted earlier, the 5 messages).

The even worse situation is how to get patch parameters when program change is initiated externally. You can only do a full bank request (RQF), which will send you all 64 patches. So imho the only solution is to:
1. send a program change (e.g. 16),
2. then do a RQF, and
3. filter the response for the previously sent program change number (in this case 16) and neglect/throw away the other 63.
I know of one editor which does it like this, but this is a stand-alone one, not a VST plugin.


I have the editor you mentioned (in fact probably all of them 😂), it's based on CTRLR, not very good and doesn't do bidirectional.

"and also place elements in their parameter order."
The parameter numbering is handled properly? Or you mean visually?

BTW: Just received my Roland MKS-70 today (with Vecoven mod), and it has the same upper/lower concept 🙄🤯😂 , but with the mod, it also sends and reacts to CC. Still patch parsing will be very similar, as well as for at least 5 or more other Roland classics. I had a really proper look around and to my knowledge nobody has been able to tackle this yet.

Markus Schlösser's icon

btw current state (with VCO2 Range and F ENV Polarity fix):

Max Patch
Copy patch and select New From Clipboard in Max.

Source Audio's icon

with correct order of parameters, I mean to place them in order they
are in sysex dump list, or id if you want so.
Makes it much easier to connect and split list of 48 items.
I tried to connect upper part to send and receive values,
look what a mess it is ...

I still don't know what your usage plan is, and will wait with any suggestions
till you post more infos about it.

Markus Schlösser's icon

Hopefully the visualizations will make things clearer:

Current state of the m4l device, works great, but problem is, that the parameters in Live do NOT represent the values in the synth. This is obviously problematic for 2 reasons: 1. I cannot bang the device/params when loading a Live set, because then all params would be overwritten, even the unchanged ones. 2. no visual clue of where things are

This is what I have been talking about mainly.
I would initiate an manual program dump on the synth (because there is no other way), Live receives all values of 1 Patch (the 5 messages), from there I can tweak on the m4l device and when reloading the set, the state of the m4l device can be send to the synth, making recall of the state feasible. Also visual feedback of all params as they actually represent the values on the synth

This is the end goal. But more complicated. The reply to a RQF is:
"Each DAT message consists of 4 sets of the Patch and Tone data. Each data set consists of 62 bytes total --- 39 bytes for Tone Parameters of a number and 23 bytes for Patch Parameters of the same number. These parameters are sent in sequence of the 'Tone Number's and 'Patch Number's. 2 DATs are sent for each 'Bank'. In normal operation, 16 DATs are totally sent for all 'bank's (1 - 8)."

But I want to start with Phase 1, that would be fine for now and also imho a good preparation for Phase 2.

Peter Ostry's icon

Markus, maybe you should rethink your patch design.

1.
Wiring each parameter individually is a nightmare. If you give names to all parameters, you can manage them as clients in [pattrstorage] and need just a few cables. Names preferably with numbers like Max does, for example MKS_[1], Mks_[2], MKS_[3] etc. This way you can copy one dial after the other and they number themselves. When you get a SysEx string in, you break it up, give the parts the numbers, send each value to [pattrstorage] and all clients are up to date. If you turn a dial, [pattrstorage] can output name and value. For example with RegEx you remove the underscore [regexp _ @substitute " "], so you have your prefix "MKS" and can route the numbers and values to the hardware. This is just a suggestion, because I've also lived in such horrible cobwebs as you for quite a long time. I'm currently working on my interface for a Waldorf Blofeld, the synth sends me 392 bytes of SysEx and I transfer 300 parameters into 300 GUI objects just by numbers. You have to be careful with the output, [pattrstorage] can be bitchy there and you have to make the actions as simple as possible. If you don't like [pattrstorage], you can at least send to the many dials wirelessly with [pattrforward]. But they must have names. After all, your patch is grown past the size where you send data "to the green box on the left side" just because you can see it.

2.
You have tons of [sxformat 240 .... 247] in your patch. They take up lots of space and must be a problem just from the weight :-) Nobody really needs this [sxformat]. You can build the variable string parts as short messages with [blabla $1 $2] or format them with [sprintf blabla %d %d], route them and add the sysex header just before you send the line to hardware.

3.
You have to deal with many parameters, the GUI becomes confusing. Source has suggested hiding certain elements when other elements should be shown. This works of course, but in the long run you will be better off with [bpatcher]. There you can use tabs and get what you need in the visible area.

For example all my 300 Blofeld parameters in a small space:

9 tabs switch the visible parameter sections. Not a single box has cables attached, that's all done by [pattrstorage]. It takes a while to convince [patterstorage] to see bpatcher clients but it works.



Here's the full SysEx workup, where the values are numbered and sent to [pattrstorage], which then updates the clients. Single values and program dumps can be distinguished by the header:

That's not much of a patch for 300 parameters with a few specialties, is it? But the Blofeld has relatively simple SysEx, I never got through that fast just with numbering.

This is how the parameters go to the hardware as SysEx when I push a knob in Max:

If you assign parameter names with numbers and (probably) have one or more [coll] to map, you don't need sxformat at all, just assemble the single string and send it out.

I hope you are not angry with me because I am not helping you with your current problem, but criticising your work. But all I wrote is only to be understood as well meant suggestions. I think that after a little familiarisation with other methods you will reach your goal much more safely. And of course, other people can help you better if your code is structured in manageable blocks.

Roman Thilenius's icon

you dont even need to use sends, you can simply use multiple instances of midiout.

Peter Ostry's icon

Yes, but not in this case.
Because I communicate with several devices, the ports are set in the startup routine. I send the values with prefixes to the output section and route the appropriate type and output there.

"BLO CC 7 90"
"BLO PC 25"
"BLO SYX 240 blah blah blah 247"

With SysEx it would be even better to leave out the whole header, Device ID and EOF. The slave at the output can stick it on and the ID could also be managed centrally. I have such settings in colls that read from the disk.

Markus Schlösser's icon

Thank you all very much for your input! It does NOT make me angry (@Peter), quite the opposite! I am grateful for the input and perspectives! :-)
I am not a developer and only started slowly learning Python 2,5 years ago to be able to use the Mackie C4 with Live (https://github.com/markusschloesser/MackieC4_P3/), so max/m4l is still very new to me (the m4l MKS80 device was my first one) and I am currently learning specifically about autopattr / pattr / pattrstorage etc in order to see if it fits my use case.
I usually learn best when I learn the way kids do: looking at what others have done and trying to replicate and adept that to my needs. Unfortunately the amount of bidirectional sysex m4l devices is very limited, I was able to find probably around 7 or so. So all your input advances my knowledge.
@Peter: which of the Blofeld m4l devices is yours?

All my dials/toggles have scripting names already (and proper short names), because I mainly manage params from the Mackie C4, which is imho the best controller for that. So using autopattr (which I need to understand better), should be relatively quick to implement. But I am still missing other basics, which I first need to understand better.

Peter Ostry's icon

"@Peter: which of the Blofeld m4l devices is yours?"

None. I don't even really know what an m4l device is :-)
I use Max only for my equipment.

Source Audio's icon

I think the difficut part here is data/controls flow.
If you have amount of dials in Live, and bind them not only to
storage / automation directy, but also into pattr system,
with it's own mind, you could overcomplicate things,
even overload your MKS by sending too many and too fast sysex strings.
That is why I prefer coll - it does nothing till one tells it so,
not store and not recall.
------
I still don't see exact goal of this.
In usual case, one has a song or whatever you call it,
and recalls few presets from midi device.
You want in addition to modulate params in real time
using sysex, and for that you need ALL dials etc to match current
state of recalled preset values.
One option is to transfer them as you do now (manualy ??? )
or you dump all banks and store it as file to read in your device,
so you send pgm message to MKS and at same time load values
from that file internally, WITHOUT passing them out to MKS.
But did you store that Dial values into Live set ?
Live Dial has init values, gets stored as part of set,
mappings, automation ... etc.
Did you draw some automation curves ?
what when/if next preset gets recalled ?

Or do you want to store a preset into live set as starting point
and dump it into MKS instead of recalling a preset ?
Can that work at all in real time without MKS hickups ??

That is logic that needs to get solved first,
it is not important if you wire that many dials manually,
or use autopattr and such, use bpatchers or whatever ese to show/hide
that many GUI elements, main thing is that you control the flow.

In some cases it would be better to disable parameter
mode of objects an use own storage.
For exampe if you ony want to recall presets and tweak params
using Mackie C4, but don't want Live to store anything of that into set.



Markus Schlösser's icon

continuing with this, I have decided to indeed just use 2 separate amount of controls for UPPER/LOWER. Mainly because my max skills are way too bad to achieve anything more complicated.
Unfortunately even with this simplification and only trying to achieve scenario/Phase 1 (manual dump from MKS80 by pressing program number), I cannot get it to work.
To reiterate, the use case is, to have the dials represent the actual values of the currently selected patch.

This is the current state:

Both unpacks go to a single dial, in both cases VCF Cutoff, once for UPPER, once for LOWER.

Before I added the extra bits on the right, VCF values were updated, but of course there was no distinction between upper and lower (which is represented by byte 7 in the original sysex sent by the MKS80, but as 240 is already removed further up the chain, I used zl nth 6).
Where am I wrong?

Source Audio's icon

I allready posted how to do that on april 10 2023 here ....
----
this here makes no sense:

1- zl.group needs size argument or bang to output.
2- using gate that way ??? what should open or close the gate ?
----------
here is again the screenshot from that post, in case you overlooked it.

connect your coll output into zl.slice 9, in place of zl.group 159.

Max Patch
Copy patch and select New From Clipboard in Max.

dump strings are based on your posted infos ....
P.S.
and important statement that 5 sysex messages always get sent in same order !

Markus Schlösser's icon

Thank you! The suggestion about using gate came from gpt4, obviously it seems to be not that good 😂

Yes, I saw your posting from April, but you wrote "P.S. if you don't need upper/lower switch for each dial separately, things are much simpler. you recall the whole string and unpack elements." So I thought, that that posting was no longer applicable. As you can see, the whole max thinking is still very new to me.
I will try to get further with your input, appreciated! :-)

Source Audio's icon

you have another mistake on the last screenshot

end of each sysex message (247) clears the coll.
you are supposed to collect all 5 dumps in single coll line.
how shoud that work ?
you need to clear coll only once - before you send the dump from
Roland.
using a coll insted of zl.group 159 is better if you need to grab
values from it later, I mean if you want to undo changes to parameters
without need to dump all again.
-------
I'll be glad to help you further with this, but you need to lay down
a good concept first, taking into account all options I mentioned
in my last post from April 15.
without that infos, it is difficult to go on with this.
P.S.

please read this too, it deals with multipe sysex messages sent without organised order from Live ....

https://cycling74.com/forums/raw-midi-values-via-midi-out-issues-with-interleaving-or-interference

Markus Schlösser's icon

"please read this too, it deals with multipe sysex messages sent without organised order from Live .... https://cycling74.com/forums/raw-midi-values-via-midi-out-issues-with-interleaving-or-interference"

Does this also apply for INCOMING sysex? So for the use case of the patch parsing I am currently working on?
WRT outgoing sysex and Live, I might have experienced this already with the MKS80 m4l device and outgoing sysex messages, but thought it was due to the fact, that sysex was never really intended for real-time control of synths, that and the possible slowness of the MKS80 and incoming sysex. But if I am careful and do not move to many dials at once (or automate them), it has been working great for the last year.

"and important statement that 5 sysex messages always get sent in same order !"


I have looked at quite a few sysex dumps from the MJS80 over the last years and have never seen another order than the one I wrote down earlier.

also to answer your previous questions:
"Or do you want to store a preset into live set as starting point and dump it into MKS instead of recalling a preset ? Can that work at all in real time without MKS hickups ??"
TBH I haven't thought about recalling when loading a previous Live set. I only thought about initating the dump from the MKS into the m4l and use that as a baseline for tweaking while in the set (and currently I usually just record that to audio then), but you are of course correct in your worries about recalling appr 130 values and sending them individually to the mks, that MIGHT be a bit too much ;-) So if I want to have a proper recall after a reload of a Live set, I would need to basically prepare a program dump in the correct format in the m4l device and then send THAT to the mks. What "proper" in this case means, I'll need to find out (might be the 5 messages that are incoming, but will check, iirc the mks80 also has some kind of edit buffer, will check in knobkraft. EDIT: I checked and it is 4 messages, the 5 from above minus the pgr (https://github.com/christofmuc/KnobKraft-orm/blob/f163a805dcb091ed6e187f71d73ffee25f7c2298/synths/roland-mks80/MKS80.cpp#L164))

Honest question: Do you think it is easier for me (given what you have seen of my max/m4l "skills"), to achieve this in Max using zl, coll etc, or would it be easier to do it in js (which I also don't know, but it seems easier to learn, coming from a somewhat stronger python base? Also with js I can use gpt and have in fact already "translated" the patch parsing for scenario 2 from C++ to js.


"you need to clear coll only once - before you send the dump from Roland."

So basically disconnect the first outlet of "t clear dump 0"? The make a button for later which clears?
EDIT: mmh tried that and that makes me get all messages 5 times, I assume because each incoming message also triggers a bang.

Thank you so much for the code! Do I need to adept this a bit because you have 15 bytes and 48 bytes (as from the spec), but what comes out of my code is without F0/F7? I actually would prefer to have the full sysex everywhere including 240 247, makes it less confusing for me, and counting is easier, but the "sel 247 240" obviously filters those out.

Also, unfortunately the program number doesn't work. The device always outputs 0, Console always shows "65 52 0 32 48 0 0 0 0",
whereas Midi-Ox shows 240 65 52 0 32 48 0 0 16 0 247
And I don't know where the 0 is coming from, will keep playing around /investigating :-)

Source Audio's icon

the problem on the link I posted affects sending of sysex messages.
You receive 5 strings in proper order, which makes no problems.
-----
sel 240 247 does filter that numbers, same as your coll aparatus does.
You are then left with header and data.
zl.slice / zl.ecils objects then separate headers from data for routing to parameter objects.
what good should be keeping 240 & 247 when routing them to dials ?
Only in case you want to store the dump for resending,
but then you should separate that 5 lines and use sysexin insed of midiin to avoid other then sysex messages to get captured.
----------
I looked at that sysex recording device you suggested in another post.
It is not good.
It does not separate sysex messages, it allows any midi message to get in, does not have any control of speed when capturing or sending sysex,
in short better not to use.
---------
code I posted parses pgm number ok

you must have a mistake somewhere else.
-------
I would stay away from js and use vanilla max objects.

All you have to take care about is to
1- connect parsed sysex dump strings to correct objects,
(which is not easy with your disordered layout)
and disable resending them back when dials get values
during dump.
2- send to Roland only parameters that really changed in case you recall
live set with dials stored, or even better, don't send them on set load.
gate at midi output would serve well for that , also while receiving the dump from Roland.
After set is loaded, you can either initiate dump from Roland, or
send controlled dump from Live to Roland, same way as any sysex librarian does.

You can capture one dump from your sysex librarian into max
to analyse it and compare with that manual patch dump from Roland.
here is sysex capture example patch, uses coll, but other storage can be used, like text, pattr, dict etc --- depends on usage

Max Patch
Copy patch and select New From Clipboard in Max.


---------
All that is easy to do, but essential is that you decide on best way to use it.
Decision are - do you want that manual dump at all ?
if not, then build a base of patch sysex banks and send them from Live to Roland.
But if you recall different programs in a Song, that would be difficult.
depends on edit buffer in mks-80, I know nothing about it,
is it multitimbral, or only 1 patch at a time ?
On JV units for example you have 16 available parts
in performance mode and each can recall own patches or get edited by sysex messages.
-----


Markus Schlösser's icon

Every time I think I am starting to get the hang of it, something comes up and messes with my theories :-)
So I took your sysex capture as inspiration and came up with this:

This doesn't work as expected, because (I think) I need to first merge the 5 messages that get stored in coll, so that they can get sliced again later. And of course I can do that, but my question is: As I have the 5 messages neatly stored in coll now anyway, wouldn't it be more sensible to go directly from there to 4 unpacks (leaving PGR out) and then recall the dumps 2,3,4,5 and direct those to the specific unpacks? With route?

Max Patch
Copy patch and select New From Clipboard in Max.

----------------------
"what good should be keeping 240 & 247 when routing them to dials ?"
Debugging! :-) With the 240 and 247 left in, it is way easier to see where dumps start and end, especially with my "messy" order ;-). I frequently use print to debug and also all documentation about the mks80 always of course includes start and end byte when counting, so always having to think about deducting start and end byte is tedious for me.
------------------------
"I looked at that sysex recording device you suggested in another post. It is not good."
It is the ONLY sysex capture m4l device out there, so there isn't much alternative ;-)
When I am finished with my device, I might do a better one
----------------------
"code I posted parses pgm number ok. you must have a mistake somewhere else."
Agreed, that's why I started fresh with your sysex capture code
-----------------------
" I would stay away from js and use vanilla max objects."
Ok, thanks for the advice!
-----------------------

"All you have to take care about is to 1- connect parsed sysex dump strings to correct objects, (which is not easy with your disordered layout) and disable resending them back when dials get values during dump. 2- send to Roland only parameters that really changed in case you recall live set with dials stored, or even better, don't send them on set load. gate at midi output would serve well for that , also while receiving the dump from Roland. After set is loaded, you can either initiate dump from Roland, or send controlled dump from Live to Roland, same way as any sysex librarian does.

You can capture one dump from your sysex librarian into max to analyse it and compare with that manual patch dump from Roland. here is sysex capture example patch, uses coll, but other storage can be used, like text, pattr, dict etc --- depends on usage"

---------
"Decision are - do you want that manual dump at all ?"
Yes, for now. It seems the easiest to implement, helps me learn more before I go further and is definitely the most helpful when working with the MKS80 right now.
---------
"if not, then build a base of patch sysex banks and send them from Live to Roland. But if you recall different programs in a Song, that would be difficult. "
I'll stick with knobkraft for librarian purposes, it is build for that, works fantastic, and additionally takes care of other things as well (bank dumps, checksum calculation etc.
---------
"depends on edit buffer in mks-80, I know nothing about it, is it multitimbral, or only 1 patch at a time ? On JV units for example you have 16 available parts in performance mode and each can recall own patches or get edited by sysex messages."
It does have an edit buffer, which you can write to by sending the 4 messages (the 5 minus PGR). Other than that, it has the "1 patch which consists of 2 tones" concept which I explained previously. Which is the 1982 equivalent of a multimode :-)

Source Audio's icon

you have several options to send data from coll to parameters.
either place copies of named coll and recall string that you need,
removing 240 247 and header.
for example for Upper APR and Tone :
1 is index of Upper APR, 3 is index of Upper tone params.

or you route it from single coll and then send / receive

or use grab.

------------
Or other ways ...
-----------------------
If you want to use extra librarian software to
1- dump patch to MKS.
then
2- dump patch from MKS to Live
3- store modified patch in Live ? and on set load dump back to MKS ?
and then again get the patch into that sysex librarian ?

I don't know if that is fastest way...
Recorder I posted was also meant to record dumps directly from that sysex librarian into max, so that you can analyse sysex flow betwen MKS and editor.

honestly, this all is not a big deal, only that many GUI elements ...
I would rather place numboxes, tabs, text buttons etc and follow
parameter order.

but that is only my own preference, simply ignore it ...




Markus Schlösser's icon

quick update to let you now where I am currently:
I am using the first method and keep the messages separately, seems more efficient and logical for me.
Started patching to all the dials and toggles and am now thinking about how to do that without messing up everything with cables :-)

Then stumbled upon a couple of things I will first need to really clarify. Some of the "global" parameters are not that global it seems, For example HOLD and GLIDE etc exist separately for Upper and Lower, so I do need to also duplicate those controls.
On the other hand the KEYMODE is really global because it defines if there even is an upper/lower

But the strange thing is that that parameter does exist in both Patch APRs. So I will need to find out why and what it does when coming from LOWER.

Also it would be nice to disable (visually) the LOWER part, when Keymode is set to WHOLE (and there's nothing to control in LOWER), but that's only optics/UX, not essential :-)

I will tinker away in the next couple of days and then come back.
Thank you so so much for all the help, explanations and code!! I am very grateful for that :-)

Source Audio's icon

here is my attempt to place params in order and wire them to
2 subpatches, and prepare sub-storage for dumped, edited values etc.
it can be simply doubled for LOWER part, addresses are easilly changed with only 2 numbers ...

Max Patch
Copy patch and select New From Clipboard in Max.


Markus Schlösser's icon

Wow this is fantastic! :-) And I lot tidier and simpler to adept for Upper/lower than mine.
Quick questions:
1.

Is this the same coll as the receiving one, or a different? I thought about this, but couldn't come to a proper conclusion.

2.

I assume this is for when loading a set and recalling values?
What does the message going into it mean/do?

3. Now with all the values in coll(s), I am still thinking about not having separated dials for upper/lower, but switching states when switching between upper and lower and recalling values from coll. But I assume this only works, when I only have 1 coll?

current state (not yet tested):

Max Patch
Copy patch and select New From Clipboard in Max.

For now I still have the training wheels on, receive/dump/send separated, but will further try to simplify UX tomorrow.

Source Audio's icon

quick answers

1- no, this is separate storage of values changed by moving dials.
Original coll should be kept intact to be able to undo changes.
this temporary coll can be then used to store to disk etc.
picture shows only 1 register, but boh shoud store data inside.

2- no this is only message to patcher to make white background.

----------
I made this version while trying to understand the sysex etc.
but had to rearange order of params and also look of it,
in max it looks terrible, unreadable when Live gui objects and fonts are used.

I am not sure if this is optimum, one could also try pattr storage
and autopattr for the dials, as Peter suggested, but that is up to you,
I explained several times my concern about unwanted resending of sysex out to Roland.
The way 2 regisers are arranged now, you can place them into bptchers,
or into singe one, and use offset message to access Upper or Lower parameters.
But I still wote for separate parameters per register ....
You have dump separated for registers, and also changed parameters
could be passed to and from the dials, sysex header woud have to be switched for sending values to Roland, it would be possible to use
only 1 set of dials if you prefer it that way.
But you will have to disable automation of dials, because
they will have no link to Upper or lower register.

Markus Schlösser's icon

ok, thank you very much!
--------------------
"But the strange thing is that that parameter does exist in both Patch APRs. So I will need to find out why and what it does when coming from LOWER."
Answer: changing the KeyMode is also possible from Lower and then affects globally.
I also needed to put those further back in terms of parameter #, as I am already above the dreaded 128 maximum parameters for Live and therefor cannot control everything anymore from my Mackie C4 controller.
---------------------
A couple of values are wrong when receiving them (Oscillator ranges, VCO2 Tune, VCO Mixer), reason being the quirky ranges and their representation in sysex. So I will need to "reverse-engineer" the logic from before. For VCO1 range this is easy (- 36), also VCO2 Tune (- 50) and VCO Mixer (- 50), but for VCO2 Range where you came up with the brilliant idea, I do not know currently (Parameter 20).
EDIT: I always forget that it needs to be "- 36" and not "-36" 🙄
---------------------
I am thinking about how I want to recall a patch that is saved in a Live set, probably not automatically. It is basically a button with a bang to the new coll, correct? And with that, I should probably build in some automation, to disable anything else, while dumping from that coll.
-----------------
Other that that, the device is fantastic, it brings a whole new experience to the MKS80 and already explored why more than before in just 2hrs of playing with it 🤩😍

Source Audio's icon

you need oposite conversion to feed the dial.

here example for VCO2 range

I assume values are 0, 36 - 84 and 100

Max Patch
Copy patch and select New From Clipboard in Max.

Markus Schlösser's icon

thank you! Implemented!

Max Patch
Copy patch and select New From Clipboard in Max.

Did a lot of optics work in order to fit it into the narrow Live lane. Might do graphical envelopes later.
I also tried to take one step out of the loading process by banging after having received the sysex dump and then automatically load into the device. Unfortunately this doesn't work (blue line). Do you know why?

Also, if I want to load a previously stored state of the device when loading a set, is it basically adding a live.thisdevice which bangs a dump to new coll, prepending 240 etc and appending 247 to the items in the new coll?

Source Audio's icon

I made mistake in my posted patch, LFO rate and LFO delay
are connected wrong.
this is proper connection:

Please check all pack - unpack connections for such mistakes.
Now to your questions :

blue patchcord should bang when coll finishes reading a file...
You are not reading any file here (at least not now), so there is no bang.
you could use that 1000ms delayed bang for that, or try this, is simpler:

Max Patch
Copy patch and select New From Clipboard in Max.


here if received messages match 5 , dump message to coll is sent.
one could bypass the length check an bang dump directly
In any case it takes less space on the screen than multiple colls...

-----------
auto send parameters to MKS ...
1- all dials will be stored as part of live set.
2- when they are populated with values, new coll also gets parameters.
3- to send out sysex messages, you have to prepend
240 + headers and append 247


You can control the speed of sysex strings so that Roland has no troubles receiving them.
Have you recorded dump from your editor into max ?
To check transmitting speed - timing between messages ?

here is coll dump patch, has 1000 ms between sysex messages

you can change it as needed, maybe it has to be faster ?

Max Patch
Copy patch and select New From Clipboard in Max.



Source Audio's icon

P.S.
would you also recall original patch used in live set
before sending stored parameters to it ?
one could send pgm change and after that sysex.

Markus Schlösser's icon

Thanks, I had already played around a lot (also with LFO1), but hadn't noticed the error, yet. Will check again tomorrow!

----------------
"blue patchcord should bang when coll finishes reading a file..."
I hadn't read till the end 🙄, I only read "data" and not "data file".
Your solution is much more elegant! Unfortunately it doesn't work, I still need to trigger the dump manually. When debugging with print, I noticed that:
print: 5
print: bang
print: 240 65 52 0 32 48 0 0 1 0 247
print: 240 65 53 0 32 48 1 0 60 50 1 2 2 0 2 0 18 1 1 0 1 55 247
print: 240 65 53 0 32 48 2 0 60 50 1 2 2 0 0 39 18 1 1 50 0 58 247
print: 240 65 53 0 32 32 1 64 1 3 0 0 0 51 1 0 0 1 0 0 0 2 60 2 1 0 60 65 2 51 37 47 0 0 1 32 0 97 98 0 100 97 1 0 0 48 48 18 7 1 57 41 100 64 84 247
print: 240 65 53 0 32 32 2 64 1 3 0 0 0 51 1 0 0 1 0 0 0 2 60 2 1 0 60 65 2 51 37 47 0 0 1 32 0 97 98 0 100 97 1 0 0 48 48 18 7 1 57 41 100 64 84 247

Seems like the bang is sent too early?
EDIT: Yep, an additional del 500 between SEL 5 and DUMP, fixes that. 💪😊
---------------
WRT auto send: Will need to test tomorrow with proper time, but thanks already!
And no, I don't think I "would you also recall original patch used in live set", that's too much of an undefined state for me, of course I could initialize with an INIT patch, but that could then potentially be sent while loading and destroying the edit buffer.
----------------------------

"one could send pgm change and after that sysex."
I don't understand this?
-----------------
"Have you recorded dump from your editor into max ? To check transmitting speed - timing between messages ?"
I haven't, but I checked the source code of the knobkraft mks80 adaptation and couldn't find anything specific, but will also test thoroughly tomorrow :-)
----------------
BTW the amount of absolutely bat shit crazy fantastic sounds I am now getting out of the MKS80 is stunning 😎🥰
Is there anything that I can help you with?

Source Audio's icon

timing in live is not same as in max, that explains additional delay needed.

when you receive dump, you also populate that dial with pgm number, wired directly to pgmout.
if that dial gets stored in live set, pgm change will be sent at unknown time. same as volume.
You just have to decide what to do about that.

enable or disable it.

keep sounding, that makes world better, and it is all help I need at the moment, thanks .