MIDI Out LED

Home Forums General Programming MIDI Out LED

Viewing 16 posts - 1 through 16 (of 16 total)
  • Author
    Posts
  • #118685
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    I can see how to hook into the panel for making an animated LED on a timer for when the panel receives MIDI, to make a MIDI IN light really easily.
    If I’m sending MIDI using sendMidiMessageNow, is there some method for displaying if the data is still being sent?
    I can’t just programmatically adding an “on led” before the sendMidiMessageNow and an “off led” immediately afterwards, becuase the send is asynchronous and happens in a different thread, doesn’t it? I want something that will continue to be on while there is still data in CTRLR’s MIDI buffer to send.

    I’m not using snapshots, this is just MIDI sysex being sent in a lump via lua. Can anybody point me in the right direction please? Thanks πŸ™‚

    #118713
    dnaldoog
    Participant
    • Topics: 4
    • Replies: 480
    • Total: 484
    • β˜…β˜…

    Not really answering the question directly, but a good visual clue for the user is uiProgressBar.

    See a working example here https://ctrlr.org/jd-990-sysex-dump-utility/

    #118726
    dnaldoog
    Participant
    • Topics: 4
    • Replies: 480
    • Total: 484
    • β˜…β˜…

    Actually come to think of it, in that panel there is a neutral 😐 smiley face icon which changes to a smiley face πŸ™‚ when the upload or download is complete (but you won’t see that icon unless you are connected to a JD-990). All you would need to do is substitute your on/off LED for that. It is achieved using timers.

    #118731
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    Thanks for the replies dnaldoog.
    Yes, I’m using a timer currently as a “kludge” but that’s highly dependent on what the current MIDI delay settings are set to, so it’s not as straightforward as working out the size for the data packet you’re about to send, although maybe that’s a better way of handling it, even if it’s a kludge.
    I guess there must be some magic formula to use to calculate the milliseconds accurately, as 31.25kBaud is a given, the only other two variables would be the delay and the size of the packet. If I made a function that acted as a wrapper around sendMidiMessageNow to instantiate the timer having looked at the size of the packet about to be sent, that would be a sort of answer, right?
    I looked at that JD panel but tu_rate doesn’t seem to go anywhere or be used for anything, so guessing there’s no “right” answer for this, if we can’t check the current MIDI output buffer queue everything we do for MIDI out is an approximation.

    #118734
    dnaldoog
    Participant
    • Topics: 4
    • Replies: 480
    • Total: 484
    • β˜…β˜…

    tu_rate=tonumber(L(panel:getLabel(“TRATE”):getText()))–[[150 seems okay 100 is too fast–]]

    is the delay for the timer. It comes from uiLabel which the user can edit.

    becuase the send is asynchronous and happens in a different thread, doesn’t it?

    These are things I don’t know much about I’m afraid.

    #118735
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    What I meant was that tu_rate doesn’t seem to be referenced after that, so I guess it was added in but never implemented?

    What I mean by the asynchronous is if you do something like:

    console("start")
    sendMidiMessageNow (stuff)
    console("end")

    then even if “stuff” is huge and would take several seconds to transfer, you’ll see “start” “end” immediately in the console. So you can’t just add some logic after the sendMidiMessageNow to turn off the led. That’s what I meant by asynchronous; it’s a “fire and forget” sort of operation that doesn’t stop your main program loop dead while it sends stuff.

    I think it sounds like the only way is to figure out how to calculate milliseconds given size of buffer to transfer and current delay settings. So it’ll be something like
    milliseconds_to_wait= (mysysexData:getSize() * magic_number )+ panelMidiGlobalDelay
    where magic_number will be the time in milliseconds for 10 bits (1 start, 8 data, 1 stop) to travel at 31.25 kBaud – the magic MIDI number.
    What the heck is magic_number…. 1/31250baud = 0.000032 seconds for a bit, 10 bits would be times ten so 0.00032 seconds per byte = 0.32 milliseconds per byte, is that right? Anybody? 0.32 is the magic_number? πŸ˜€ I’ll try it later an report back. Think you might have just helped steer me here onto something, dnaldoog!

    #118738
    dnaldoog
    Participant
    • Topics: 4
    • Replies: 480
    • Total: 484
    • β˜…β˜…

    I think that panel code is confusing.

    Here is a watered down version with a timer that triggers an LED to switch on when the upload is complete. Also included is a progress bar. ?

    progressbar

    • This reply was modified 2 months, 1 week ago by dnaldoog.
    Attachments:
    You must be logged in to view attached files.
    #118740
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    Thanks for taking the time to do that – I’ll try it later.

    #118742
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    c takes a time to initialise, but I get what it’s doing – like you said earlier, it’s not answering the original question but it is a really neat way of doing a progress bar.
    But my problem isn’t sending lots of little packets, the Poly800 expects a giant floater of a packet in one go – depending upon which version it’s anywhere from 3273 bytes to 5383 bytes – in one single go. This actually takes over a second to upload, and during that time I’ve basically got to try and let the user know it’s happening. But becuase it could be big or small, it can’t be a fixed number.
    But I just tried this and it worked:

        mysysexData=MemoryBlock();
         mysysexData=encodeSysex();
         s = mysysexData:getSize();
    
        totwait = tonumber( ( s * 0.32 ) + 5 );
    
        -- console("start timer40...");
        panel:getModulatorByName("sendDumpButton"):getComponent():setProperty ("uiButtonTextColourOff","FFFF4545",true); -- set to red
        timer:startTimer (40, totwait); 
    
        panel:sendMidiMessageNow(CtrlrMidiMessage(mysysexData:toHexString(1))); -- go send it
    

    My callback basically stops the timer and sets the text back to black, but it’s bang on timing now. The trick was that “magic number’ and if you multiply the size of your sysex by 0.32 and add the current mididelay time onto it (in my case 5ms) it’s only bang on perfect πŸ™‚

    So hoping this might help someone else out – two ways actually, your very nice bar progress for split packets or a precise LED on time for very big packets.

    #118747
    dnaldoog
    Participant
    • Topics: 4
    • Replies: 480
    • Total: 484
    • β˜…β˜…

    So is this what you are doing? Sending the MIDI should be in the timer function, right?

    
    timerCallback = function(timerId) -- this function name must match the name above in the first line. ;)
    
    if timerId==40 then
        panel:sendMidiMessageNow(CtrlrMidiMessage(mysysexData:toHexString(1))); -- go send it
        panel:getModulatorByName("sendDumpButton"):getComponent():setProperty ("uiButtonTextColourOff","FFFF4545",true); -- set to red
        timer:stopTimer(timerId)
    end
    
    end
    
    myMethod=function()
    --x = a large sysex string
     mysysexData=MemoryBlock(x);
         --mysysexData=encodeSysex(); -- What is this?
         s = mysysexData:getSize();
        panel:getModulatorByName("sendDumpButton"):getComponent():setProperty ("uiButtonTextColourOff","000000",true); -- set to black etc
        totwait = tonumber( ( s * 0.32 ) + 5 );
        -- console("start timer40...");
    timer:setCallback (40, timerCallback)
    timer:startTimer (40, totwait); 
    end --f
    
    #118748
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    No, like I said above, I think there are two different use cases here – your one where you have little tiny packets you can break up, one at a time – in which case yes, you’d put the send in the timer function.
    My use case is completely different – I have one single big packet that I can’t split. So putting it in the timer wouldn’t accomplish anything – I can’t split it, so I can’t do the bar thing, or fire off the packets one at a time to the timer.
    In my code fragment above, encodesysex is the bad boy that puts together all the controllers into one sysex string – the Poly800 doesn’t understand CC’s, so it can only take one big sysex load.
    My problem was this – sendMidiMessageNow is ASYNCHRONOUS. So putting one giant packet send in the timer would be meaningless, as it would just send it and return instantaneously, then spend 2-3 seconds doing the upload of that giant packet. BUT – and this was the breakthrough moment – if I can accurately calculate the exact time it takes to send that giant packet, I can tell the timer EXACTLY how long to make the LED red. So the timer callback, as above, is purely the place where it makes the LED black at the end of the 2-3 second send. Does that make sense now? There are two different use cases.
    Yours works for split packets – not for giant floaters πŸ˜€
    To prevent any more confusion, here is the use case for big packets in full:

    function sendSysex()
    
    -- This stops index issues during panel bootup
    	if panel:getRestoreState() or panel:getBootstrapState() then
    		return;
    	end
    
        local mysysexData, my_c0, programChange, tempProg, j, currMidi, s, totwait;
    
    	if timer:isRegistered(40) == false then
    	    -- console ("timer40 not registered, do it now");
    		timer:setCallback(40, timer40Callback);
    	end
    
    	if timer:isTimerRunning(40) then
    		-- console ("timer40 already running, stop it");
    		timer:stopTimer(40);
    	end
    
    -- update sysex and add crc
        mysysexData=MemoryBlock();
    	
        mysysexData=encodeSysex();  -- create the full sysex dump
    
        s = mysysexData:getSize();
    
        totwait = tonumber( ( s * 0.32 ) + 5 ); -- 5ms for the global delay, totwait in ms
    
        -- console("start timer40...");
        panel:getModulatorByName("sendDumpButton"):getComponent():setProperty ("uiButtonTextColourOff","FFFF4545",true); -- make it red
        timer:startTimer (40, totwait); 
    
        panel:sendMidiMessageNow(CtrlrMidiMessage(mysysexData:toHexString(1)));
    
    end
    
    function timer40Callback()
        panel:getModulatorByName("sendDumpButton"):getComponent():setProperty ("uiButtonTextColourOff","FF454545",true); -- back to black
    	timer:stopTimer(40);
        -- console("stop timer40");
    end
    
    #118750
    dnaldoog
    Participant
    • Topics: 4
    • Replies: 480
    • Total: 484
    • β˜…β˜…

    I totally get it now! It’s a great idea. I guess I wasn’t quite getting what you were trying to do and I usually have to think twice about the concept of timers each time I approach them anyway. In my code all that would achieve is delay sending the MIDI! Duh!

    #118755
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    It’s all cool, dude! Both methods have their use. In fact, I’m minded now to try mixing both approaches by making a bar on my giant floater.. I can’t split the sysex, but if I know for example after the size calculation it will take exactly 2000ms to send, I could divide the total time number by 4 and fire the timer every (2000/4)=500ms to say 0%.. 25%… 50%.. 75%.. done – once I have done that “magic number’ calculation the rest is easy. Just the bar – not the midi! I still wish there was a way to actually interrogate the midi out buffer directly, but I think these are all good solutions πŸ™‚

    #118756
    samoht
    Participant
    • Topics: 1
    • Replies: 54
    • Total: 55
    • β˜…

    Maybe the Juce MidiBuffer Class can be helpful:

    MidiBuffer::isEmpty() const
    Returns true if the buffer is empty.

    #118758
    computerchemist
    Participant
    • Topics: 2
    • Replies: 31
    • Total: 33
    • β˜…

    Maybe the Juce MidiBuffer Class can be helpful:

    Yes, I found that some time back but I’m not sure how it is exposed through Ctrlr. Have you got any worked Ctrlr examples to look at? My suspicion is that atom probably needs to do some C++ binding magic.

    #118765
    samoht
    Participant
    • Topics: 1
    • Replies: 54
    • Total: 55
    • β˜…

    Have you got any worked Ctrlr examples to look at?

    No πŸ™

    My suspicion is that atom probably needs to do some C++ binding magic.

    Yes, it seems so.

Viewing 16 posts - 1 through 16 (of 16 total)
  • The forum ‘Programming’ is closed to new topics and replies.
There is currently 0 users and 27 guests online
No users are currently active
Forum Statistics
Threads: 2,460, Posts: 17,299, Members: 54,253
Most users ever online was 12 on January 22, 2019 3:47 pm
Ctrlr