Help with Sysex please

Home Forums General General MIDI discussion Help with Sysex please

Tagged: 

This topic contains 23 replies, has 7 voices, and was last updated by Possemo Possemo 1 month ago.

Viewing 20 posts - 1 through 20 (of 24 total)
  • Author
    Posts
  • #11260
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    Hi, I hope someone can point me in the right direction please.

    I have been trying to use sysex in my panel to get two way editing, although I have been researching how to program with sysex my results have been unsuccessful.

    Maybe if someone could show me how to code for my synth I would understand where I am going wrong. I am trying to control the DCO 1 tune on a Kiwi-3P, this is what I have entered in the sysex field of the modulator:

    [f0] [7f 7f 7f] [60] [01] [00] [04] [15] [0xxxxxxx] [f7]

    Below are the sysex notes from the Kiwi-3P manual.

    _______________________________________________________________________________

    From the KiwiTechnics Manual:
    SYSEX HEADER
    Notes $nn = Hexadecimal Data – Decimal data is in Brackets e.g. $0a (10)
    $f0 Sysex Start
    $7f $7f $7f Non Specific Manufacturers ID
    $60 Kiwitechnics ID
    $01 Kiwitechnics 3P ID
    nn Device ID ($00-$0f)(3P Device ID 1-16)
    xx Command ID (see table 1.0)
    $01=Request Global Dump
    $02=Transmit/Receive Global Dump
    $03=Request Tone Edit Buffer Dump
    $04=Transmit/Receive Tone Edit Buffer Dump
    $05=Request Tone Dump
    $06=Transmit/Receive Tone Dump
    $07=Request Pattern Dump
    $08=Transmit/Receive Pattern Dump
    $09=Request Seq Dump
    $0a=Transmit/Receive Seq Dump
    $0b=Request Tone Name
    $0c=Transmit/Receive Tone Name
    $0d=Request Tone Parameter
    $0e=Transmit/Receive Tone Parameter
    $0f=Request Global Parameter
    $10=Transmit/Receive Global Parameter
    $11=Request Pattern Edit Buffer Dump
    $12=Transmit/Receive Pattern Edit Buffer Dump
    $13=Request Sequence Edit Buffer Dump
    $14=Transmit/Receive Sequence Edit Buffer Dump
    Data Depending on command type (see table 1.0)
    $f7 Sysex Footer
    _______________________________________________________________________________

    $04 (4) Transmit/Receive Tone Edit Buffer Dump
    Null x 2 + 128 data bytes
    2 x null bytes sent followed by 128 bytes of data in the following format
    $00-$13 (0-19) = Tone Name Ascii Bytes Tone Name
    $14 (20) = DCO1 Wave/Range 0000yyxx xx = DCO 1 Range
    00 = 16′
    01 = 8′
    10 = 4′
    yy = DCO 1 Wave
    00 = Saw
    01 = Pulse
    10 = Sqr
    $15 (21) = DCO1 Tune 0xxxxxxx (0-127) Split Control +-63
    (+- 1 octave or +-1200 cents)

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #11268
    msepsis
    msepsis
    Participant
    • Topics: 217
    • Replies: 731
    • Total: 948
    • ★★★

    As far as I know, you don’t want to represent 0-127 as binary in your sysex formula.. replace the 0xxxxxxx with simply “xx” and loose the brackets. Your formula should look just like this:

    f0 7f 7f 7f 60 01 00 04 15 xx f7

    place that formula into the sysex formula property of a ctrlr slider (referred to as a “modulator” in ctrlr) and set the midi message type to “sysex”. the value of the slider will go where the “xx” is in your formula.

    Im not positive this will work as I haven’t built panels for synths that notate parameters in binary but it should do the trick, or at least someone can correct me if I’m wrong here.

    Monstrum Media | Music, Sound & Software Design, Chicago / San Francisco listen

    #11280
    atom
    atom
    Moderator
    • Topics: 156
    • Replies: 2940
    • Total: 3096
    • ★★★★★

    The Roland JUNO106 uses Sysex to do bi-directional editing. You can use that as an example.

    #11282
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    Many thanks msepsis, your response let me know I was working somewhere in the right ball park.

    I entered the formula as you specified but it locked up the patch on my 3P. So I fiddled about a bit. Your comment about binary made me reconsider the formula, so I checked what MIDI the 3P was sending out when in edit mode (via MIDI Monitor). It seemed that the 04 and the 15 where replaced by b0 and 0a in the formula from the 3P. So I tried this:

    f0 7f 7f 7f 60 01 00 b0 0a xx f7

    It worked! the results looked like this:
    [22:49:02:000181]: [System exclusive] RAW:[f0 7f 7f 7f 60 01 00 b0 0a 44 f7]
    [22:49:02:000281]: [System exclusive] RAW:[f0 7f 7f 7f 60 01 00 b0 0a 43 f7]
    [22:49:02:000315]: [System exclusive] RAW:[f0 7f 7f 7f 60 01 00 b0 0a 42 f7]
    [22:49:03:000678]: [System exclusive] RAW:[f0 7f 7f 7f 60 01 00 b0 0a 40 f7]

    Next stage is to see if this method will also apply to a DCO Range uiCombo popup menu.

    Many thanks msepsis for your assistance and pointing me in the right direction.

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #11283
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    The Roland JUNO106 uses Sysex to do bi-directional editing. You can use that as an example.

    Hi Atom, I have indeed been looking at your Juno 106 panel as an example of what I wish to achieve. What confused me initially was the considerable differences in Manufacturer IDs between Roland and KiwiTechnics. Once msepsis had confirmed that part was correct I felt confident to explore the formula further.

    I should say many thanks for putting Ctrlr out there, brilliant software. I am using the panel in CC at present and it has enabled me to start getting sounds together very quickly.

    One question regarding sysex, I’m hoping that the panel will be able to reflect patch changes made on the 3P. Will this require extra scripting or settings to be made in the Ctrlr panel?

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #11291
    msepsis
    msepsis
    Participant
    • Topics: 217
    • Replies: 731
    • Total: 948
    • ★★★

    One question regarding sysex, I’m hoping that the panel will be able to reflect patch changes made on the 3P. Will this require extra scripting or settings to be made in the Ctrlr panel?

    yes.. you will need to get familiar with getting data and assigning values to modulators (knobs, sliders, buttons, etc).. Basically what you need to do is create a program change knob (or two buttons, – and +) that send a multimessage to your synth. first message is a program change, second message is a sysex program dump request.

    Then on your panel itself (just click on “blank” space on your panel) go to the properties on the far right and look for “called when the panel receives a MIDI message”.

    You’ll need to create a Lua method there called something like midiMessageRecieved. You need to create some hook that determines whether the message coming in is a program dump. Best ways to do this is ether by getting the nth byte to see if it is equal to your synth’s message ID for a program dump, or get the size of the message to compare to what a program dump’s byte length is. Here’s some example code of getting the 4th byte to see if it is equal to 0x10 (0x before a number represents HEX) which in my synths’s case indicates the message is a program dump from the synth:

    
    midiMessageReceived = function(midiMessage)
    
    --this is a comment, it is ignored by the lua interpreter
    --create a variable "IDM" which is the value of the 4th byte of any incoming MIDI message:
    
    IDM = midiMessage:getLuaData():getByte(4)
    
    --is the fourth byte of incoming midi message equal to 0x10?
    		if IDM == 0x10 then
    --if yes, then run another method called "assignValues"
    				assignValues(midiMessage)
    --this next line is just a console message that alerts you that the script has run this far. View these in the "Lua Console"
    				console ("program change sent, standby for panel update...")
    		end
    end
    

    IF the message is a program dump from the synth, you’ll then get the bytes that contain the values for your parameters, assign them to a variable (each one) and then assign that variable as the value for each modulator on your panel.
    Here’s a shortened example of my “assignValues” method:

    function assignValues(midiMessage)
    
    -- create a variable that contains all bytes of program data from the synths program dump, get this info from synth's manual:
    	programData = midiMessage:getData():getRange(7,256)
    
    -- cool, we've got everything, all we need to do now is actually assign the values to the modulators.
    --NOTE: use "setValueMapped" if the modulator is a combobox with custom values.
    --use "setModulatorValue" if the modulator is a typical modulator with a value range of, say 0-127 
    
    	panel:getModulatorByName("Osc 1 Octave"):setValueMapped(programData:getByte(1), false)
    	panel:getModulatorByName("Osc 1 Semitone"):setValueMapped(programData:getByte(2), false)
    	panel:getModulatorByName("Osc 1 Detune"):setModulatorValue(programData:getByte(3), false, false, false)
    
    --continue for ALL parameters within your program dump.. . 
    
    end

    Monstrum Media | Music, Sound & Software Design, Chicago / San Francisco listen

    #11312
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    Many thanks msepsis, a very clear explanation.

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #11318
    drsteve
    drsteve
    Participant
    • Topics: 3
    • Replies: 33
    • Total: 36

    I tried this: f0 7f 7f 7f 60 01 00 b0 0a xx f7

    It worked! the results looked like this:
    [22:49:02:000181]: [System exclusive] RAW:[f0 7f 7f 7f 60 01 00 b0 0a 44 f7]

    I am a newbie to ctrlr, but have worked with sysex and midi for decades. I never knew that you could send a byte with the most significant bit set as data within a sysex command. Perhaps none of the gear I ever had used it. Hmmmmmm. Something new to explore.

    #12021
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    OK, not had as much time to work on this lately. However the panel is sending the Tone Buffer Edit Dump request then the Lua method is hooking up with the resulting Sysex response as msepsis instructed above. But then the values from the Lua assign method are not responding as I would expect.

    For example a DCO1 Tune that should be set at 64 by the Lua assign ends up placed at 32 on one patch and then 115 on the next.

    Any ideas on where I am getting it wrong would be very welcome.

    Here’s the dump request:
    [System exclusive] Ch:[–] No:[—-] RAW:[f0 7f 7f 7f 60 01 00 03 f7]

    This is what the 3P returns:
    [System exclusive] Ch:[–] No:[—-] RAW:[f0 7f 7f 7f 60 01 00 04 00 01 48 69 67 68 20 53 74 72 69 6e 67 73 20 20 20 20 20 20 20 20 01 3f 3f 00 3f 01 40 29 3f 00 3f 00 3f 0a 00 57 00 00 43 3b 3f 00 00 13 1f 61 13 1f 1f 61 1c 01 68 18 01 79 18 00 2f 03 41 00 00 3f 00 00 00 00 03 0c 00 00 00 00 6f 00 00 00 00 00 3f 3f 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 f7]

    This is how the KiwiTechnics manual describes the first few lines of data:
    $04 (4) Transmit/Receive Tone Edit Buffer Dump
    2 x null bytes sent followed by 128 bytes of data in the following format

    Data $00-$13 / Byte (0-19) = Tone Name / Data Type Ascii Bytes / Data Details Tone Name
    Data $14 / Byte (20) = DCO1 Wave/Range / Data Type 0000yyxx / Data Details xx =DCO 1 Range 00=16′ 01=8’10=4′ yy =DCO 1 Wave 00=Saw 01=Pulse 10=Sqr
    Data $15 / Byte (21) = DCO1 Tune / Data Type 0xxxxxxx / Data Details (0-127) Split Control +-63 (+- 1 octave or +-1200 cents)
    Data $16 / Byte (22) = DCO1 Env Mod Amount / Data Type 0xxxxxxx / Data Details (0-127) Split Control +-63

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #12022
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    Better post the assign method too:

    assignValues = function(midiMessage)
    
    programData = midiMessage:getData():getRange(1,112)
    
    	-- DCO 1 Wave/Range here (19)
    	panel:getModulatorByName("DCO 1 Frequency"):setModulatorValue(programData:getByte(20), false, false, false)
    	panel:getModulatorByName("DCO 1 Env Mod Amt"):setModulatorValue(programData:getByte(21), false, false, false)
    	panel:getModulatorByName("DCO 1 LFO Mod Amt"):setModulatorValue(programData:getByte(22), false, false, false)
    	-- Byte (23) Not used
    	-- DCO 2 Wave/Range here (24)
    	panel:getModulatorByName("DCO 2 Frequency"):setModulatorValue(programData:getByte(25), false, false, false)
    	panel:getModulatorByName("DCO 2 Fine Tune"):setModulatorValue(programData:getByte(26), false, false, false)
    	panel:getModulatorByName("DCO 2 Env Mod Amt"):setModulatorValue(programData:getByte(27), false, false, false)
    	panel:getModulatorByName("DCO 2 LFO Mod Amt"):setModulatorValue(programData:getByte(28), false, false, false)
    	-- Byte (29) Not used
    	panel:getModulatorByName("Cross Mod"):setValueMapped(programData:getByte(30), false)
    	panel:getModulatorByName("DCO Source Mix"):setModulatorValue(programData:getByte(31), false, false, false)
    
    end

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #12023
    atom
    atom
    Moderator
    • Topics: 156
    • Replies: 2940
    • Total: 3096
    • ★★★★★

    You must remember that getData() returns the entire message as a memory block, so you’ll get any “headers” and sysex prefixes that you should bypass to get to the actual data.

    Well here is how i was able to extract the program name, you should be able to go on from this (i hardcoded the MIDI data into the lua method)

    	-- Your method code here
    
    	data = "f0 7f 7f 7f 60 01 00 04 00 01 48 69 67 68 20 53 74 72 69 6e 67 73 20 20 20 20 20 20 20 20 01 3f 3f 00 3f 01 40 29 3f 00 3f 00 3f 0a 00 57 00 00 43 3b 3f 00 00 13 1f 61 13 1f 1f 61 1c 01 68 18 01 79 18 00 2f 03 41 00 00 3f 00 00 00 00 03 0c 00 00 00 00 6f 00 00 00 00 00 3f 3f 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 f7"
    
    	mb = MemoryBlock(data)
    	r = mb:getRange(8,130)
    	console ("--------------------")
    	console (r:getRange(2,16):toString()) -- 2 null bytes
    

    You can just paste this into Lua console, and it should print

    --------------------
    High Strings
    
    • This reply was modified 5 years, 8 months ago by atom atom.
    • This reply was modified 5 years, 8 months ago by atom atom.
    #12026
    atom
    atom
    Moderator
    • Topics: 156
    • Replies: 2940
    • Total: 3096
    • ★★★★★

    Also i noticed that the KIWI manual says it might start transmitting garbage data, keep an eye for that, if that starts to happen you should do a memory wipe according to the manual.

    #12029
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    You must remember that getData() returns the entire message as a memory block, so you’ll get any “headers” and sysex prefixes that you should bypass to get to the actual data.

    Many thanks for the prompt response Atom. I took me a while to understand your reply, but in essence I take to mean that I should include the headers in the byte count so:
    panel:getModulatorByName(“DCO 1 Frequency”):setModulatorValue(programData:getByte(20), false, false, false)
    becomes:
    panel:getModulatorByName(“DCO 1 Frequency”):setModulatorValue(programData:getByte(32), false, false, false)

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #12031
    atom
    atom
    Moderator
    • Topics: 156
    • Replies: 2940
    • Total: 3096
    • ★★★★★

    Yeah you need to calculate your byte positions in the data. Very often the manufacturer will say “byte 0” and that means byte 0 in the data section, that’s actualy 6th or 7th or whatever byte in the message. More often they don’t count the heading “F0” as part of the message, they assume it’s like a prefix and you should ignore it.

    You need to read their minds, or just do what i do and go through a hard process of try-and-fail, many many times.

    Also index is not byte number, byte numer 1 is index 0 and so on.

    #12032
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    Again many thanks. I am totally new to programming so I copied msepsis’ code not fully understanding what the instructions are doing! For example I now realise…

    programData = midiMessage:getData():getRange(1,112)

    should be..

    programData = midiMessage:getData():getRange(10,128)

    Most of the modulators correctly reflect the patch now, it seems the comboboxes are still not changing.

    Also some of the combobox bytes are expressed like this:
    Byte (20) DCO1 Wave/Range, Data Type 0000yyxx, xx =DCO 1 Range 00=16′ 01=8’10=4′ yy =DCO 1 Wave 00=Saw 01=Pulse 10=Square
    Wave and range being two separate comboboxes, I guess that the bits have to be assigned individually?

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #12039
    atom
    atom
    Moderator
    • Topics: 156
    • Replies: 2940
    • Total: 3096
    • ★★★★★

    Yeah it should be simple. take a byte you need to split(read bits from it) and read them using BigInteger()

    I’m writing from my head so it might not compile but it should bring you on the right path

    
    byte = data:getByte(0)
    bi = BigInteger(byte)
    
    part1 = bi:getBitRangeAsInt(0,2) -- Data Type 0000yyxx - XX part
    part2 = bi:getBitRangeAsint(2,2) -- Data Type 0000yyxx - YY part
    
    
    • This reply was modified 5 years, 8 months ago by atom atom.
    #12051
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    Well, I have got so far. The code doesn’t crash the panel. Syntax was bugging me, as I don’t fully understand what I am doing so I missed a few things initially.

    byte = programData:getByte(20) 
    bi = BigInteger(byte)
    
    panel:getModulatorByName("DCO 1 Range"):setValueMapped(bi:getBitRangeAsInt(0,2), false)
    panel:getModulatorByName("DCO 1 Waveform"):setValueMapped(bi:getBitRangeAsInt(2,2), false)

    It works a treat, however I had to change the Combo Contents to this:

    00=16
    01=8
    10=4

    from this:

    16=0
    8=32
    4=64

    Now I am going to look at some putting Lua in the Combobox to make this conversion.
    Cheers Dude : )

    • This reply was modified 5 years, 8 months ago by lfo2vco lfo2vco.
    • This reply was modified 5 years, 8 months ago by lfo2vco lfo2vco.
    • This reply was modified 5 years, 8 months ago by lfo2vco lfo2vco.
    • This reply was modified 5 years, 8 months ago by lfo2vco lfo2vco.
    • This reply was modified 5 years, 8 months ago by lfo2vco lfo2vco.

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

    #13445
    msepsis
    msepsis
    Participant
    • Topics: 217
    • Replies: 731
    • Total: 948
    • ★★★

    Also some of the combobox bytes are expressed like this:
    Byte (20) DCO1 Wave/Range, Data Type 0000yyxx, xx =DCO 1 Range 00=16′ 01=8’10=4′ yy =DCO 1 Wave 00=Saw 01=Pulse 10=Square
    Wave and range being two separate comboboxes, I guess that the bits have to be assigned individually?

    Ok now after helping you with assigning values from program dumps I’m actually a little stumped here myself with understanding the most efficient way, given the same data expression as lfo2vco stated above I’m stuck with how to send values from the PANEL to the synth..

    So, taking the same example:
    0000yyxx
    if yy = DCO 1 Wave
    and xx = DCO 1 Range

    what do you give your DCO 1 Wave and DCO 1 Range knob/combobox to send the data? I’m imagining you need to do it with lua, both modulators get the same script – Script must involve getting the current values of both DCO 1 Wave and Range, putting them together then sending out the combined values from bin to hex in the defined sysex formula for those combined values. I can visualize what needs to happen but I need a nudge or an example so I know how to express this correctly and efficiently.

    I checked out the Juno 106 (i used to own a 6, loved that thing) panel but only saw two scripts that were not involved at all in any way.

    • This reply was modified 5 years, 7 months ago by msepsis msepsis.

    Monstrum Media | Music, Sound & Software Design, Chicago / San Francisco listen

    #13454

    Hecticcc
    Participant
    • Topics: 25
    • Replies: 160
    • Total: 185
    • ★★

    I suggest you take a look at the amt8 panel i made, it is in the panels section.

    All switches are used to set specific bits in 16 different bytes, those are then sent to a method that makes and transmits the midimessage.

    #13468
    lfo2vco
    lfo2vco
    Participant
    • Topics: 25
    • Replies: 159
    • Total: 184
    • ★★

    Hi Msepsis & Hecticcc, thanks for the follow up. Atom talked me through this one and together we got it sorted. But for completeness and also in case anyone else follows this thread looking for an answer here is the method used.

    Some of the combobox bytes are expressed like this in the Kiwi-3P user manual:
    Byte (20) DCO1 Wave/Range,
    Data Type 0000yyxx,
    xx =DCO 1 Range 00=16′ 01=8’10=4′
    yy =DCO 1 Wave 00=Saw 01=Pulse 10=Square

    So the on receiving the MIDI message from the synth Lua treats it like this:

    byte = programData:getByte(20)
    bi = BigInteger(byte)
    panel:getModulatorByName("DCO 1 Range"):setValueMapped(bi:getBitRangeAsInt(0,2), false)
    panel:getModulatorByName("DCO 1 Waveform"):setValueMapped(bi:getBitRangeAsInt(2,2), false)

    And to get the data into Byte 20 for a Sysex dump Lua treats it like this:

    dco1range = panel:getModulatorByName("DCO 1 Range")
    dco1waveform = panel:getModulatorByName("DCO 1 Waveform")
    bi = BigInteger(0)
    bi:setBitRangeAsInt (0,2,dco1range:getValue())
    bi:setBitRangeAsInt (2,2,dco1waveform:getValue())
    byte20 = bi:getBitRangeAsInt(0,7)

    I get a bit of a buzz from doing this kind of stuff with data, perhaps because I am new to the game… perhaps because I am just a bit odd : )

    Here is some noise I organised into an acceptable format:
    https://soundcloud.com/lfo2vco/a-dark-crystal

Viewing 20 posts - 1 through 20 (of 24 total)

You must be logged in to reply to this topic.

There is currently 2 users and 33 guests online
presergasulviwa, faitatumidfure
Forum Statistics
Threads: 2,205, Posts: 15,547, Members: 54,692
Most users ever online was 12 on January 22, 2019 3:47 pm
Do NOT follow this link or you will be banned from the site!