what do you need to know in order to make panels?

Home Forums General Programming what do you need to know in order to make panels?

Viewing 20 posts - 1 through 20 (of 46 total)
  • Author
    Posts
  • #33261
    reklamchef
    Participant
    • Topics: 18
    • Replies: 40
    • Total: 58

    I don’t know what LUA is, but I dont know if i have years to learn this or that random programming language, so it would be great to know what one needs to know in order to make a panel. Is LUA necessary for this? I reached my max google searches for the day.

    “you need a basic understanding of C++” would be a good answer for example.

    at this time, it is not clear how to proceed, but there seems to be loads of users who are making panels. what do they know that the thousands of users who are not making panels do not? a short list would be fine. and LUA just popped out of nowhere in 2014, never heard of that before.

    • This topic was modified 5 years, 11 months ago by reklamchef.
    #33263
    atom
    Participant
    • Topics: 157
    • Replies: 2943
    • Total: 3100
    • ★★★★★

    Lua is not needed for a basic panel (two way control of parameters), if you need to implement program dumps and some more complex data exchange between the device and Ctrlr then you will need Lua.

    Lua is a scriptin language, it’s fast simple and very popular (i know that there is a lot of lua in the gaming world)

    #33265
    reklamchef
    Participant
    • Topics: 18
    • Replies: 40
    • Total: 58

    what is needed to make things for ctrlr? c++ or java or html etc?

    #33266
    atom
    Participant
    • Topics: 157
    • Replies: 2943
    • Total: 3100
    • ★★★★★

    If you wish to help with the development of the app itself it’s all C++, 90% of the code is written in the JUCE Library, 10% is luabind/boost (boost is used only for luabind and is not used anywhere in the code of core Ctrlr)

    There are some platform specific snips of ANSI C that are needed to do syscalls on each OS but this are real small pieces of code.

    There are some helper scripts for building and generating some headers, those are written in bash/awk.

    No other programming language/technology is used.

    #33296
    wikter
    Participant
    • Topics: 5
    • Replies: 49
    • Total: 54

    Just open a sample ensemble and take a look at the edit mode. It,s really easy to understand how cc’s or sysex are sent.
    Then try an easy job. Send notes from a panel, then CC,s…
    Seems very complex but is just like a mixer console, once you’re done to one channel, doesn’t care how many.

    MVXSynths
    R3 / MOX6 / Electribe2 / Electribe R MkII / Electribe MX / K4R / BCR2000 / XStation61 / XStation25 / Virus Rack / Chameleon / Proteus 2500 / RM1X / KN2600

    #33534
    layzer
    Participant
    • Topics: 6
    • Replies: 36
    • Total: 42

    climb the highest moutain you can find, perform the voodoo ritual and ask the LUA Gods to grant you the knowledge

    #33619
    m.tarenskeen
    Participant
    • Topics: 30
    • Replies: 113
    • Total: 143
    • ★★

    I also want to learn The Art Of Ctrlr Panel Creation. I guess the best way is to just start and see what problems I have to solve to reach my goal.

    For my first project I have choosen the Korg DS8 (any other Korg DS-8 or 707 owners in this forum?). And I am making some progress already 🙂

    First question. I can copy/paste a modulator (slider, rotary etc) or a group including a set of controllers which is a real timesaver since I have two more or less identical groups of parameters for Oscillator 1 and 2.

    But after pasting, the VST-index numbers are not automatically changed, so I have many controllers sharing the same VST-index number that I have to manually change to give them each unique numbers. Goodbye timesaver!

    Is this a bug or o feature? If it is a feature I would like to see a way to automatically renumber the VST-index numbers.

    #33693
    atom
    Participant
    • Topics: 157
    • Replies: 2943
    • Total: 3100
    • ★★★★★

    It’s just the default behavior in Ctrlr, i think there was a fix for that but it looks as if didn’t help much. What version of Ctrlr are you using ?

    #33697
    m.tarenskeen
    Participant
    • Topics: 30
    • Replies: 113
    • Total: 143
    • ★★

    I am using (according to Help->About)

    Version = 5.3.26, Build date = Wed Dec 3 16:01:40 CET 2014, Branch = Nightly, Juce = 3.1.0

    #33715
    atom
    Participant
    • Topics: 157
    • Replies: 2943
    • Total: 3100
    • ★★★★★

    I just uploaded a new nightly build that should fix this issue.

    #33740
    m.tarenskeen
    Participant
    • Topics: 30
    • Replies: 113
    • Total: 143
    • ★★

    The issue still exists after this update, at least that is what I experience:

    1. I create a group, but some rotary sliders in it
    2. I right-click on the group and select “copy with children”
    3. I paste
    4. I check the VST index numbers: The VST index numbers in the pasted group are identical to the ones in the original group.

    #33753
    atom
    Participant
    • Topics: 157
    • Replies: 2943
    • Total: 3100
    • ★★★★★

    You might be right i just fixed the situation when you create/copy/paste single components, i’ll have a look at the groupping stuff asap.

    #33758
    atom
    Participant
    • Topics: 157
    • Replies: 2943
    • Total: 3100
    • ★★★★★

    I uploaded a new version again, it should fix the group indexing issue.

    #33924
    m.tarenskeen
    Participant
    • Topics: 30
    • Replies: 113
    • Total: 143
    • ★★

    Help wanted!
    My first Ctrlr project (a Korg DS-8 panel) has now reached a point where I will have to start understanding how to use Lua in Ctrlr. There are some basic things I want to do. I can’t be that difficult, because almost every existing panel should be able to do this:

    A1. Receive a SysEx dump with the parameters from one complete Single Voice from my synth via MIDI. (For example when using a SysEx dump request message)
    A2. Read the databytes from the dump, if necesary first do some rearranging from the bits and bytes in the dumpdata, and then pass the indivual parameter values to the corresponding modulators in my Ctrlr panel.
    A3. The panel now reflects the actual data in my synth

    B1. Read the state of all the controller values in my panel.
    B2. If necessary to some rearranging with the bits and bytes to put the data in the correct SysEx dump format, maybe add a SysEx checksum.
    B3. Send the paneldata as a Single Voice SysEx dump to the synth via MIDI: The synth now reflects the data in my Ctrlr panel.

    Looking how other panels work does help, but I really could use some “getting started” advice to speed up my learning process. Once I can understand and grasp the basic mechanisms I can find out the detailed Lua coding and syntax details that are specific for my synth’s SysEx internals myself.

    I hope someone can give me some usable advice!

    #34107
    wikter
    Participant
    • Topics: 5
    • Replies: 49
    • Total: 54

    Yes, that’d be great…

    MVXSynths
    R3 / MOX6 / Electribe2 / Electribe R MkII / Electribe MX / K4R / BCR2000 / XStation61 / XStation25 / Virus Rack / Chameleon / Proteus 2500 / RM1X / KN2600

    #34128
    dasfaker
    Keymaster
    • Topics: 79
    • Replies: 790
    • Total: 869
    • ★★★

    This is a very basic guide to manage sysex dumps.

    In theory your panel is finished and you created a modulator for each byte from the dump.

    Now you need to request a dump from the synth. You need to know the sysex command for this. This is an example dump request, that can be added to the LUA code of a button, for instance:

    m = CtrlrMidiMessage({0xF0, 0x00, 0x20, 0x33, 0x01, 0x10, 0x30, 0x00, 0x00, 0xF7}) -- REQUEST PATCH MESSAGE
    panel:sendMidiMessageNow(m) -- SENDS THE DUMP REQUEST MESSAGE

    Once you send the dump request, the synth will respond sending a sysex message containing the data of the patch. This message usually contains a header followed by the parameters of the synth engine, and this is the part you need to manage. This is an reduced example of a sysex patch:

    
    f0 00 20 33 01 00 10 00 00 0c 01 02 7d 00 00 00 00 00 00 40 00 00 00 00 00 00 40 00 00 40 60 40 00 00 40 f7

    You need to know where the actual data of patch starts, in this case is byte 9 (0c)(starting from the first byte that is number 0).

    In order to manage the sysex dump received, you need to create a LUA method for “Called when panel receives a MIDI message” panel property. This is a basic method for this:

    midiMessageReceived = function(midiMessage)
    	s = midiMessage:getSize() -- Size of the midi dump received
    	if s == 524 then -- if size match the expected size of the dump requested
    		PatchDataLoaded = midiMessage:getData() -- create a memoryblock with the data dump
    		programData 	= midiMessage:getData():getRange(09,514) -- create a memory block with the synth engine data, leaving the header
    		assignValues(midiMessage,false) -- call a script to assign each byte to each modulator.
    	end
    end

    Now the assignValues script will look like this:

    function assignValues(midiMessage,var)
    	panel:getModulatorByName("Portamento"):setModulatorValue(programData:getByte(0), false,var,false) -- assign the first byte of programData memoryBlock to it's correspondent modulator
    panel:getModulatorByName("Osc1 Volme"):setModulatorValue(programData:getByte(1), false,var,false) -- assign the second byte of programData memoryBlock to it's correspondent modulator. 
    .
    .
    .
    -- And so on till the last byte.
    end

    Of course, you need to know the target modulator for each byte of the dump.

    Now to send a patch to the synth, you can do it in several ways: you can send each modulator individually, or collect all modulator values in a memoryBlock and send it to the synth in the same form the panel will receive a dump patch.

    To do it individually, just create a button and add this lua method:

    
    function sendPatch()	
    
    	a = panel:getModulatorByName("Portamento"):getValue() -- get the value of this modulator
    	panel:getModulatorByName("Portamento"):setModulatorValue(a,false,true,false) -- set the same value for this modulator, sending it to the synth
    	a = panel:getModulatorByName("Osc1 Volume"):getValue() -- get the value of this modulator
    	panel:getModulatorByName("Osc1 Volume"):setModulatorValue(a,false,true,false) -- set the same value for this modulator, sending it to the synth
    .
    .
    .
    -- and so on till the last modulator.
    end

    Your modulators could be arranged in some way to do this more easy. In the following case, modulators where created in the same order they have in a dump message (so modulator with index 0 corresponds to dump’s byte 0 and so on)

    function sendPatch()
    	for n = 0, 514, 1 do -- the amount of modulators to send 
    		a = panel:getModulatorByIndex(n) -- get modulator with index n			
    		b = a:getModulatorValue() -- get it's value
    		a:setModulatorValue(b, false,true,false) -- set it's value, sending it to the synth.
    	end
    end

    If you want to send the patch to the synth as a single dump the procedure is similar; collect modulator values, add them to a memoryBlock and send the memoryBlock as a sysex message:

    
    function sendPatch()
    	a = panel:getModulatorByName("Portamento"):getValue() -- get the value of this modulator
    	PatchDataLoaded:setByte(9,a) -- write it in the memory block in the correct byte
    	a = panel:getModulatorByName("Osc1 Volume"):getValue()
    	PatchDataLoaded:setByte(10,a)
    .
    .
    .
    
    	m = CtrlrMidiMessage(PatchDataLoaded:toHexString(1)) -- convert the memory block in a midi message
    	panel:sendMidiMessageNow(m) -- send the midi message to the synth
    end

    Hope that helps.

    #34129
    m.tarenskeen
    Participant
    • Topics: 30
    • Replies: 113
    • Total: 143
    • ★★

    Tnak you! This an answer that will really really help! I will print it out as a getting-started reference 🙂

    I noticed there are a lot of good questions posted in this this forum but few of them are really answered.

    Now I can start writing the necessary Lua code for my panel. One tricky (typical for KORG synths) part will be to convert 7-bit MIDI data to 8-bit internal Korg data and vice versa. I should write a dedicated function for that, so that it can be re-used for probably every Korg synth since the M1 and DS-8/707. I have written such a function Python ( for another project:http://dxconvert.martintarenskeen.nl ) that I now need to port to LUA.
    Korg uses a system where 7 bytes of 8-bit data are remapped to 8 bytes of 7-bit data before they are stored in a SysEx bulk dump, using a special method.

    #34132
    dasfaker
    Keymaster
    • Topics: 79
    • Replies: 790
    • Total: 869
    • ★★★

    In fact a lot of questions are answered. This question has been around several times, and answered several times (I remember a big post from msepsis), but it’s true that it’s hard to find where it is, it could be spread across many posts or even threads.

    About the Korg thing I can’t help you, I don’t own any Korg and none of my panels do bit conversions.

    #34207
    m.tarenskeen
    Participant
    • Topics: 30
    • Replies: 113
    • Total: 143
    • ★★

    I’m making progress:-)
    An additional question: I have created a uiLCDLabel in my panel to display a 10-character patchname. In the MIDI bulkdump there are 10 bytes used for the patchname. How do I construct MIDI bytes from the uiCDLabel and vice versa? (The MIDI bytes in my case do not represent ASCII values in my case. For ASCII values 32 to 127 a MIDI value of 0 to 85 is used instead.)

    #34208
    dasfaker
    Keymaster
    • Topics: 79
    • Replies: 790
    • Total: 869
    • ★★★

    To pass the name of a label to modulators with patch name bytes, I put a script in label’s property “called when the label content changes”.

    Those are simplified lines from my scripts, so it could lack something

    b = panel:getModulatorByName("GLOBAL Patch Name") -- Label with patch name
    if b ~= nil then
    	c = b:getComponent()
    	namePatch = c:getProperty("uiLabelText")
    	namePatchLength = string.len(namePatch)
    
    	if namePatchLength > 0 then
    		character = string.byte(namePatch), 1) 
    		panel:getModulatorByName("CMON NameChar 1"):setValue(character,true) -- -- in your case you should do some maths
    	else
    		panel:getModulatorByName("CMON NameChar 1"):setValue(32,true) -- space char
    	end
    	if namePatchLength > 1 then
    		character = string.byte((namePatch), 2)
    		panel:getModulatorByName("CMON NameChar 2"):setValue(character,true)  -- modulator with first character byte
    	else
    		panel:getModulatorByName("CMON NameChar 2"):setValue(32,true)
    	end
    	.
    	.
    	.
    	end
    end

    This is how I pass the bytes of a dump to a label

    -- custom ascii character list, modify it to suit your needs.
    	symbols = {"|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|","|"," ","!","","#","$","%","&"," ","(",")","*","+","4","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","b","]","^","_","

    “,”a”,”b”,”c”,”d”,”e”,”f”,”g”,”h”,”i”,”j”,”k”,”l”,”m”,”n”,”o”,”p”,”q”,”r”,”s”,”t”,”u”,”v”,”w”,”x”,”y”,”z”,”{“,”|”,”}”,”~”}

    — get bytes from dump
    char1 = symbols[programData:getByte(240)]
    char2 = symbols[programData:getByte(241)]
    char3 = symbols[programData:getByte(242)]
    char4 = symbols[programData:getByte(243)]
    char5 = symbols[programData:getByte(244)]
    char6 = symbols[programData:getByte(245)]
    char7 = symbols[programData:getByte(246)]
    char8 = symbols[programData:getByte(247)]
    char9 = symbols[programData:getByte(248)]
    char10 = symbols[programData:getByte(249)]

    text1 = string.format(“%s%s%s%s%s%s%s%s%s%s”, char1, char2, char3, char4, char5, char6, char7, char8, char9, char10)
    patchName = panel:getModulatorByName(“GLOBAL Patch Name”) — label with patch name
    if patchName ~= nil then
    c = patchName:getComponent()
    c:setPropertyString (“uiLabelText”, text1)
    end`

    PS: The list of characters get fucked with the wordpress code, I hope you get the point of the list.

    • This reply was modified 5 years, 10 months ago by dasfaker.
    • This reply was modified 5 years, 10 months ago by dasfaker.
    • This reply was modified 5 years, 10 months ago by dasfaker.
    • This reply was modified 5 years, 10 months ago by dasfaker.
Viewing 20 posts - 1 through 20 (of 46 total)
  • You must be logged in to reply to this topic.
There is currently 1 user and 32 guests online
Tedjuh
Forum Statistics
Threads: 2,442, Posts: 17,213, Members: 52,905
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!