Deep-Symmetry / beat-link-trigger

Trigger events and automate shows in response to events on Pioneer CDJs
Eclipse Public License 2.0
443 stars 37 forks source link

0.6.2 on Linux - Not sending MIDI #113

Closed whoman321 closed 3 years ago

whoman321 commented 3 years ago

Hello!

beat-link-trigger-0.6.2 Ubuntu 18.04 Oracle Java 15

I'm not able to output MIDI with beat-link-trigger-0.6.2 on Ubuntu 18.04 (also tested on Debian Stretch).
Everything appears to work properly in beat-link-trigger, but when I try "Simulate->Activate" on a trigger, there is no midi sent, and the log doesn't show any errors.

I decided to try Oracle's JRE (Ubuntu comes with openJDK by default), so I downloaded the Oracle JDK version 15, same thing. Everything seems to be working, but no MIDI is sent out.

2020-Dec-04 17:15:24 INFO [beat-link-trigger.logs:80] - Beat Link Trigger version 0.6.2-1-0xe8eb built Wed May 13 01:26:08 UTC 2020
2020-Dec-04 17:15:24 INFO [beat-link-trigger.logs:81] - Java version 15, Java HotSpot(TM) 64-Bit Server VM, Oracle Corporation
2020-Dec-04 17:15:24 INFO [beat-link-trigger.logs:82] - Operating system version Linux 5.4.0-54-generic
2020-Dec-04 17:15:24 INFO [beat-link-trigger.logs:83] - Log files can grow to 200000 bytes, with 5 backlog files.
2020-Dec-04 17:15:24 INFO [beat-link-trigger.core:177] - Beat Link Trigger starting.
2020-Dec-04 17:15:25 INFO [beat-link-trigger.core:97] - Trying to go online, Use Real Player Number? false
2020-Dec-04 17:15:26 INFO [beat-link-trigger.core:114] - Giving up attempt to go online, user wants to continue offline.
2020-Dec-04 17:15:46 INFO [beat-link-trigger.triggers:380] - Reporting activation: Note 12 on channel 3
2020-Dec-04 17:15:49 INFO [beat-link-trigger.triggers:407] - Reporting deactivation: Note 12 on channel 3

I've tested with 3 different USB Midi adapters. Beat link Trigger does detect them and lists them on the "Output" field, but nothing is sent. These MIDI adapters work 100% fine with other linux software on my laptop.

image

I added '-verbose' to the java command, and tested again. Here's the lines of output that occur when I click on Activate.

[58.886s][info][class,load] beat_link_trigger.util$create_device_update_packet$fn__5822 source: file:/home/tangent/Downloads/beat-link-trigger-0.6.2.jar
[58.887s][info][class,load] clojure.core$map_indexed$mapi__8548 source: file:/home/tangent/Downloads/beat-link-trigger-0.6.2.jar
[58.887s][info][class,load] clojure.core$map_indexed$mapi__8548$fn__8549 source: file:/home/tangent/Downloads/beat-link-trigger-0.6.2.jar
[58.888s][info][class,load] sun.awt.X11.XQueryTree source: jrt:/java.desktop
[58.889s][info][class,load] overtone.midi$midi_find_device$fn__880 source: file:/home/tangent/Downloads/beat-link-trigger-0.6.2.jar

These lines only appear the first time I click Activate. Subsequent clicks don't produce anything on the verbose output.

Just based on that last line, I'm thinking maybe overtone was having trouble connecting to the USB MIDI adapter, so I searched for info about overtone's midi-clj on Linux, and from what I'm finding it works perfectly fine on Ubuntu 18.04.

Is there anything I could do to debug this problem further? Am I using the wrong Java version?

Thank you for your time

brunchboy commented 3 years ago

Hi! I’m sorry you’ve run in to this, but unfortunately I can’t share any insight. It sounds like you are doing everything right. In your verbose output, I don’t see any evidence of problems; those lines just show that some classes are being loaded. That will happen only the first time you try to use them. As far as Beat Link Trigger is concerned, everything seems to be working fine. The line [58.889s][info][class,load] overtone.midi$midi_find_device$fn__880 source: file:/home/tangent/Downloads/beat-link-trigger-0.6.2.jar probably reflects the first time the anonymous function inside midi-clj’s midi-find-device function is being used. (Clojure functions are represented as Java classes so they can be passed around as first-class language constructs and used with higher-order functions.)

I would suggest trying to write a minimal Java program to send MIDI to those devices, and see if you can get it working, without the added layers of Clojure, midi-clj, and Beat Link Trigger. If not, then you can open a support case with Oracle (or whichever Java distribution you end up trying to use), or with your MIDI interface vendors. If that does work, then we can start adding layers and seeing where the failure is occurring. For example, see if it’s possible to write a short standalone Clojure program to send a MIDI note to the device.

I don’t have any MIDI interfaces or Ubuntu systems handy to try to help investigate this, I’m afraid.

brunchboy commented 3 years ago

Perhaps this weekend I could try hooking up one of my USB MIDI controllers to my Raspberry Pi, and see if I can get one of the buttons to light up from a BLT trigger as a check that Linux MIDI can sometimes work. That wouldn’t help much understand why it isn’t working in your case, though.

Taking a step back, what is the integration you are trying to accomplish? Is there an interface other than MIDI, such as perhaps OSC or some other IP-based protocol, that you might be able to try instead?

whoman321 commented 3 years ago

Thank you so much for your quick response, and willingness to help!

I've done very little practical Java programming in the past (I mostly use C, C++, and Python) but will try your suggestion of making a super simple java app to test with midi output, just to make sure it's not a problem with Java, and to take it one layer at a time.

If you do end up testing it with your RPi, yes that would be great to know if that works or not.

Unfortunately, at the moment, MIDI is the only interface I am trying to use with beat-link-trigger, as the main piece of hardware I'm trying to trigger is currently MIDI only. Tomorrow I'll see if I can set up some sort of OSC listener to test it with, but that would just be for debugging.

Once I get MIDI triggers working, in the future I'm definitely looking at using some other hardware/software that would possibly use OSC or even some of your custom trigger implementations (perhaps Beyond or grandma2), but for now MIDI is what I really need.

I'll report back in the next day or two about my luck with the pure java midi output test.

Thanks again!

whoman321 commented 3 years ago

Found this example which locates a MIDI device, and then outputs the contents of a MIDI file. Tested it on Linux... It works, so it's not a Java issue.

import javax.sound.midi.*;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class midiOut {

    public static void main(String[] args) throws IOException, MidiUnavailableException, InvalidMidiDataException
    {

        while(true) {
            //List Midi Devices
            MidiDevice.Info[] midiInfo = MidiSystem.getMidiDeviceInfo();
            int i = 0;
            for (MidiDevice.Info info : midiInfo) {
                System.out.println(i + ": " +info.getName());
                i+=1;
            }
            //Select Device
            Scanner in = new Scanner(System.in);
            int input = in.nextInt();

            if(input == -1)
            {
                break;
            }
            //Get Device and Open it
            MidiDevice selectedDevice = MidiSystem.getMidiDevice(midiInfo[input]);
            System.out.println(selectedDevice.getDeviceInfo().getName());
            selectedDevice.open();
            //Get Sequencer and Receiver, load the File and start playing
            Receiver receiver = selectedDevice.getReceiver();
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.getTransmitter().setReceiver(receiver);
            sequencer.open();
            sequencer.setSequence(MidiSystem.getSequence(new File("/home/tangent/oneV/CDJ/JavaMidi/axel.mid")));
            sequencer.start();

            sequencer.addMetaEventListener(new MetaEventListener() {
                @Override
                public void meta(MetaMessage meta) {
                    if(meta.getType() == 47)
                    {
                        sequencer.close();
                    }
                }
            });

            while (sequencer.isOpen()){}
        }
    }
}

So now I've installed Clojure and Lein / lein-try, and will try to test MIDI hardware output in REPL. Can you let me know which specific Clojure library is used in BLT? Overtone, or Midi-clj?

Edit:
I see, it's: [overtone/midi-clj "0.5.0"] Will try it out and report back

brunchboy commented 3 years ago

That’s good news! And yes, midi-clj is from overtone.

brunchboy commented 3 years ago

I’m hugely curious about what is going on here.

whoman321 commented 3 years ago

I'm having some trouble setting up a development environment with midi-clj 0.5.0 (Ubuntu can be finiky with specific package versions sometimes)

Currently with the defualt packages provided for Ubuntu 18.04, I can only get REPL (lein try) loaded with version 0.1 of the midi-clj library.

I will continue to troubleshoot why 0.5 of midi-clj is not available/working for my 'lein try' development environment, but in the meantime, if it is possible for you to create a .jar file with a simple clj application that does the following, that would allow me to further debug this problem regardless of my progress on resolving my development environment issues..

The USB MIDI adapter is named "CH345". The midi-clj app would just need to set that device for MIDI out, then trigger a 'note' on that device:

midi-clj:

(use 'midi)

; Select midi input and output devices
; These functions bring up a GUI chooser window to select a midi port
(def keyboard (midi-in))
(def phat-synth (midi-out))

; Once you know the correct device names for your devices you can save the
; step of opening up the GUI chooser by putting a unique part of the name
; as an argument to midi-in or midi-out.  The first device with a name that
; matches with lookup is returned.
(def ax (midi-in "axiom"))

; Connect ins and outs easily
(midi-route keyboard phat-synth)

; Trigger a note (note 40, velocity 100)
(midi-note-on phat-synth 40 100)
(Thread/sleep 500)
(midi-note-off phat-synth 0)

I will continue to try to get my devel environment working with 0.5 of midi-clj, but if it's easy for you to create a jar file from the info specificed above (with version 0.5), please do so and I'll report back with my results immediately.

If you need any more info, please let me know. Thank you again for your time.

brunchboy commented 3 years ago

I am a little confused, but perhaps that is because I’ve never heard of lein-try. Did you install leiningen itself? If so, then you should just be able to clone the Beat Link Trigger repository, cd into that directory, and type lein repl to end up at a REPL prompt with all of the libraries used by Beat Link Trigger, including the right version of midi-clj. Then at the REPL prompt, you can (require '[overtone.midi :as midi]) and then interactively try things like picking your interface, (def adapter (midi/midi-out)) (will pop up a GUI window to choose it), and then (midi/midi-note-on adapter 40 100).

If you really need me to build a smaller jar file, I could, but that is not a normal or effective way to do Clojure development and experimentation. Leiningen will use the Maven package management infrastructure to find the dependencies enumerated in the project’s project.clj file, independent of Ubuntu’s own package management ideas.

In your example above, I can’t see what’s in your project.clj or equivalent, but I can’t imagine one that would make the (use 'midi) expression work. (In general, use is deprecated these days in favor of the more explicit use of require and package aliases, to make it clear where functions are coming from, but even beyond that, I don’t see how use in your example is supposed to understand that midi refers to the package named overtone.midi.)

Also, these days the standard Clojure packages themselves have their own notions of dependency management, so if you want to avoid the complexity of Leiningen itself, I could put together an example of how to set up a minimal deps.edn file so you could use clj to open a REPL with access to midi-clj.

brunchboy commented 3 years ago

Oh, right, getting a REPL up in BLT requires Antora to be installed these days, so we might be better off pursuing the minimal deps.edn approach. I need to step away for a minute but I will work on putting together that example when I get a chance.

brunchboy commented 3 years ago

OK, version 1, which should allow me to build a jar if you need. With Leiningen installed, create a new directory, cd into it, and create the file project.clj with the following content:

(defproject midi-test "0.1.0-SNAPSHOT"
  :description "Experimenting with midi-clj"
  :dependencies [[org.clojure/clojure "1.10.1"]
                 [overtone/midi-clj "0.5.0"]])

While still in a shell in that directory, run lein repl. That will open a REPL prompt, evaluating the following expressions should work for you:

(require '[overtone.midi :as midi])  ; Load midi-clj, alias as "midi".
(def adapter (midi/midi-out))  ; Use GUI to select adapter, bind to variable.
(midi/midi-note-on adapter 40 100)  ; Send a note to that adapter.
(midi/midi-note-off adapter 40)  ; Silence the note we sent (optional).

This works on my machine, does it for you? Next I will see about building an executable Jar from it in case you can’t install Leiningen for some reason.

brunchboy commented 3 years ago

OK, that was easy. By simply running lein uberjar I was able to create a jar for you that runs the REPL. Download this, unzip it (only because GitHub doesn’t recognize that jar files are already zip files) and then when you run java -jar followed by the path to the jar, you will get a REPL where you can test the above four expressions.

midi-test-0.1.0-SNAPSHOT-standalone.jar.zip

brunchboy commented 3 years ago

Finally, for completeness (and because I promised to), here is how you would do it without using Leiningen, but just using a current Clojure installation. Again, create an empty directory, cd in to it, then create the file deps.edn with the following content:

{:deps
 {overtone/midi-clj {:mvn/version "0.5.0"}}}

Then you can issue the command clj to run Clojure and open a REPL, at which point you can evaluate the four test expressions shown above.

whoman321 commented 3 years ago

Ahhh, yes I see. I was the one that was a bit confused... I was using leiningen incorrectly. Thank you so much for the example and the steps on executing everything.

I have to take care of a few things right now, but will be back in front of the computer in about an hour, and will report back with the results of the above mentioned example.

whoman321 commented 3 years ago

I downloaded the jar file and executed the steps you mentioned... and it works. the light on my USB MIDI adapter blinks whenever I execute a midi/midi-note-on (or off) command.

I then tested the BLT jar again, and it doesn't work. Very strange.

...not sure where to go from here.
Any suggestions?

whoman321 commented 3 years ago

I decided to try to put the command into a 'Custom' trigger in BLT, just to see if the result would be different, but I'm guessing my syntax is not correct.:

(midi/midi-note-on trigger-output 40 100)

image

brunchboy commented 3 years ago

It’s complaining there that trigger-output does not have a valid MIDI destination associated with it. So, I am wondering if your triggers are somehow not properly attached to the devices you want them to be… It would be worth examining the entire contents of the BLT log from the point of startup to where your attempt to send the MIDI note fails. You might also want to try re-selecting the device in the MIDI Output menu.

brunchboy commented 3 years ago

If a trigger is assigned to a device that doesn’t exist, it still displays that device name, although it’s supposed to show a red “Not Found” indicator at the bottom right end of the trigger where the Enabled menu goes, as described in the User Guide, and that still works on my system. I wonder if it is somehow getting confused about what MIDI devices exist on yours?

whoman321 commented 3 years ago

I created a new Trigger, set it to custom with the mentioned command, tried to 're-select' the 'Midi Output'. Since there's only one item on the dropdown list, there's no way to de-select it and select it again.

2020-Dec-06 17:05:52 INFO [beat-link-trigger.logs:80] - Beat Link Trigger version 0.6.2-1-0xe8eb built Wed May 13 01:26:08 UTC 2020
2020-Dec-06 17:05:52 INFO [beat-link-trigger.logs:81] - Java version 15, Java HotSpot(TM) 64-Bit Server VM, Oracle Corporation
2020-Dec-06 17:05:52 INFO [beat-link-trigger.logs:82] - Operating system version Linux 5.4.0-54-generic
2020-Dec-06 17:05:52 INFO [beat-link-trigger.logs:83] - Log files can grow to 200000 bytes, with 5 backlog files.
2020-Dec-06 17:05:52 INFO [beat-link-trigger.core:177] - Beat Link Trigger starting.
2020-Dec-06 17:05:53 INFO [beat-link-trigger.core:97] - Trying to go online, Use Real Player Number? false
2020-Dec-06 17:05:55 INFO [beat-link-trigger.core:114] - Giving up attempt to go online, user wants to continue offline.
2020-Dec-06 17:06:49 INFO [beat-link-trigger.triggers:380] - Reporting activation: Custom 127 on channel 1
2020-Dec-06 17:06:49 ERROR [beat-link-trigger.triggers:148] - Problem running Trigger 2 Activation Expression:
(midi/midi-note-on trigger-output 40 100)
                                             java.awt.EventDispatchThread.run  EventDispatchThread.java:   90
                                      java.awt.EventDispatchThread.pumpEvents  EventDispatchThread.java:  101
                                      java.awt.EventDispatchThread.pumpEvents  EventDispatchThread.java:  109
                          java.awt.EventDispatchThread.pumpEventsForHierarchy  EventDispatchThread.java:  113
                             java.awt.EventDispatchThread.pumpEventsForFilter  EventDispatchThread.java:  124
                          java.awt.EventDispatchThread.pumpOneEventForFilters  EventDispatchThread.java:  203
                                            java.awt.EventQueue.dispatchEvent           EventQueue.java:  742
java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege     ProtectionDomain.java:   85
                                  java.security.AccessController.doPrivileged     AccessController.java:  391
                                                    java.awt.EventQueue$5.run           EventQueue.java:  743
                                                    java.awt.EventQueue$5.run           EventQueue.java:  745
java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege     ProtectionDomain.java:   95
java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege     ProtectionDomain.java:   85
                                  java.security.AccessController.doPrivileged     AccessController.java:  391
                                                    java.awt.EventQueue$4.run           EventQueue.java:  715
                                                    java.awt.EventQueue$4.run           EventQueue.java:  721
                                        java.awt.EventQueue.dispatchEventImpl           EventQueue.java:  772
                                             java.awt.Component.dispatchEvent            Component.java: 4822
                                            java.awt.Window.dispatchEventImpl               Window.java: 2769
                                         java.awt.Container.dispatchEventImpl            Container.java: 2307
                                 java.awt.LightweightDispatcher.dispatchEvent            Container.java: 4489
                             java.awt.LightweightDispatcher.processMouseEvent            Container.java: 4548
                            java.awt.LightweightDispatcher.retargetMouseEvent            Container.java: 4919
                                             java.awt.Component.dispatchEvent            Component.java: 4822
                                         java.awt.Container.dispatchEventImpl            Container.java: 2321
                                         java.awt.Component.dispatchEventImpl            Component.java: 4990
                                              java.awt.Container.processEvent            Container.java: 2263
                                              java.awt.Component.processEvent            Component.java: 6379
                                     javax.swing.JComponent.processMouseEvent           JComponent.java: 3342
                                         java.awt.Component.processMouseEvent            Component.java: 6614
                                   java.awt.AWTEventMulticaster.mouseReleased  AWTEventMulticaster.java:  297
                 javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased      BasicMenuItemUI.java: 1056
                               javax.swing.plaf.basic.BasicMenuItemUI.doClick      BasicMenuItemUI.java: 1012
                                           javax.swing.AbstractButton.doClick       AbstractButton.java:  369
                                    javax.swing.DefaultButtonModel.setPressed   DefaultButtonModel.java:  262
                           javax.swing.DefaultButtonModel.fireActionPerformed   DefaultButtonModel.java:  405
                           javax.swing.AbstractButton$Handler.actionPerformed       AbstractButton.java: 2308
                               javax.swing.AbstractButton.fireActionPerformed       AbstractButton.java: 1967
      seesaw.action.proxy$javax.swing.AbstractAction$ff19274a.actionPerformed                                
                                                      seesaw.action/action/fn                action.clj:   90
                 beat-link-trigger.triggers/create-trigger-row/sim-actions/fn              triggers.clj:  871
                                 beat-link-trigger.triggers/report-activation              triggers.clj:  390
                              beat-link-trigger.triggers/run-trigger-function              triggers.clj:  146
                         beat_link_trigger.expressions$eval136$fn__138.invoke            NO_SOURCE_FILE:    1
                                                   overtone.midi/midi-note-on                  midi.clj:  253
                                                   overtone.midi/midi-note-on                  midi.clj:  257
                                                  overtone.midi/midi-send-msg                  midi.clj:  248
java.lang.NullPointerException: Cannot invoke "javax.sound.midi.Receiver.send(javax.sound.midi.MidiMessage, long)" because "sink" is null

I will try with two devices plugged in, and switch between the two.

When I unplug the USB, enabled changes to NOT FOUND. When I plug it back in, it does not go back to the 'Enabled' options.

whoman321 commented 3 years ago

I think you are right. when I have both plugged in, and then launch BLT, both devices appear in the dropdown, and the Enabled option is good. When I switch the MIDI Output to the 2nd adapter, it changes to NOT FOUND. Then when I change it back to the first adapter, it still says NOT FOUND.

brunchboy commented 3 years ago

Ah, I think you are running into an issue with the fact that Java MIDI is not capable of coping with devices being hot-plugged while an application is running. I actually helped write a replacement MIDI service provider for Java under macOS called CoreMIDI4J which fixes other problems with Java MIDI on the Mac, and makes it able to cope with dynamic changes to the MIDI environment. Unless/until someone writes something like that for Linux, you will need to have all your MIDI devices connected when you start Beat Link Trigger, and not add or remove any while it is running. (Or you will need to quit and relaunch it so that Java can re-scan the MIDI environment).

We’re getting to the point where it might be easier to discuss this on the Gitter channel though?

brunchboy commented 3 years ago

Another experiment that might be interesting is to add a Global Setup Expression that runs (def adapter (midi/midi-out)) and then in your Custom Activation Expression use (midi/midi-note-on adapter 40 100) and see how stable/long lived that connection seems to be. There may be something going on with the way BLT is caching long-lived opened MIDI outputs which is clashing with something about how Linux Java MIDI expects them to be used.

whoman321 commented 3 years ago

getting an error when adding that to Global Setup Expression. I tried with double parentheses and single parentheses.

Log:

2020-Dec-06 17:30:22 INFO [beat-link-trigger.logs:80] - Beat Link Trigger version 0.6.2-1-0xe8eb built Wed May 13 01:26:08 UTC 2020
2020-Dec-06 17:30:22 INFO [beat-link-trigger.logs:81] - Java version 15, Java HotSpot(TM) 64-Bit Server VM, Oracle Corporation
2020-Dec-06 17:30:22 INFO [beat-link-trigger.logs:82] - Operating system version Linux 5.4.0-54-generic
2020-Dec-06 17:30:22 INFO [beat-link-trigger.logs:83] - Log files can grow to 200000 bytes, with 5 backlog files.
2020-Dec-06 17:30:22 INFO [beat-link-trigger.core:177] - Beat Link Trigger starting.
2020-Dec-06 17:30:23 INFO [beat-link-trigger.core:97] - Trying to go online, Use Real Player Number? false
2020-Dec-06 17:30:24 INFO [beat-link-trigger.core:114] - Giving up attempt to go online, user wants to continue offline.
2020-Dec-06 17:40:32 ERROR [beat-link-trigger.editors:841] - Problem parsing Global Setup Expression
                                               java.awt.EventDispatchThread.run     EventDispatchThread.java:   90
                                        java.awt.EventDispatchThread.pumpEvents     EventDispatchThread.java:  101
                                        java.awt.EventDispatchThread.pumpEvents     EventDispatchThread.java:  109
                            java.awt.EventDispatchThread.pumpEventsForHierarchy     EventDispatchThread.java:  113
                               java.awt.EventDispatchThread.pumpEventsForFilter     EventDispatchThread.java:  124
                            java.awt.EventDispatchThread.pumpOneEventForFilters     EventDispatchThread.java:  203
                                              java.awt.EventQueue.dispatchEvent              EventQueue.java:  742
  java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege        ProtectionDomain.java:   85
                                    java.security.AccessController.doPrivileged        AccessController.java:  391
                                                      java.awt.EventQueue$5.run              EventQueue.java:  743
                                                      java.awt.EventQueue$5.run              EventQueue.java:  745
  java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege        ProtectionDomain.java:   95
  java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege        ProtectionDomain.java:   85
                                    java.security.AccessController.doPrivileged        AccessController.java:  391
                                                      java.awt.EventQueue$4.run              EventQueue.java:  715
                                                      java.awt.EventQueue$4.run              EventQueue.java:  721
                                          java.awt.EventQueue.dispatchEventImpl              EventQueue.java:  772
                                               java.awt.Component.dispatchEvent               Component.java: 4822
                                              java.awt.Window.dispatchEventImpl                  Window.java: 2769
                                           java.awt.Container.dispatchEventImpl               Container.java: 2307
                                   java.awt.LightweightDispatcher.dispatchEvent               Container.java: 4489
                               java.awt.LightweightDispatcher.processMouseEvent               Container.java: 4548
                              java.awt.LightweightDispatcher.retargetMouseEvent               Container.java: 4919
                                               java.awt.Component.dispatchEvent               Component.java: 4822
                                           java.awt.Container.dispatchEventImpl               Container.java: 2321
                                           java.awt.Component.dispatchEventImpl               Component.java: 4990
                                                java.awt.Container.processEvent               Container.java: 2263
                                                java.awt.Component.processEvent               Component.java: 6379
                                       javax.swing.JComponent.processMouseEvent              JComponent.java: 3342
                                           java.awt.Component.processMouseEvent               Component.java: 6614
org.pushingpixels.substance.internal.utils.RolloverButtonListener.mouseReleased  RolloverButtonListener.java:  108
                       javax.swing.plaf.basic.BasicButtonListener.mouseReleased     BasicButtonListener.java:  279
                                      javax.swing.DefaultButtonModel.setPressed      DefaultButtonModel.java:  262
                             javax.swing.DefaultButtonModel.fireActionPerformed      DefaultButtonModel.java:  405
                             javax.swing.AbstractButton$Handler.actionPerformed          AbstractButton.java: 2308
                                 javax.swing.AbstractButton.fireActionPerformed          AbstractButton.java: 1967
        seesaw.action.proxy$javax.swing.AbstractAction$ff19274a.actionPerformed                                   
                                                        seesaw.action/action/fn                   action.clj:   90
                               beat-link-trigger.editors/build-update-action/fn                  editors.clj: 1205
                beat-link-trigger.editors/create-triggers-editor-window/save-fn                  editors.clj: 1257
                           beat-link-trigger.editors/update-triggers-expression                  editors.clj:  829
                        beat-link-trigger.editors/update-triggers-expression/fn                  editors.clj:  834
                            beat-link-trigger.expressions/build-user-expression              expressions.clj:  412
                                                              clojure.core/eval                     core.clj: 3214
                                                                            ...                                   
                             beat-link-trigger.expressions/wrap-user-expression              expressions.clj:  390
                      beat-link-trigger.expressions/gather-convenience-bindings              expressions.clj:  365
                                                          clojure.walk/postwalk                     walk.clj:   53
                                                              clojure.walk/walk                     walk.clj:   47
                                                             clojure.core/doall                     core.clj: 3148
                                                             clojure.core/dorun                     core.clj: 3142
                                                              clojure.core/next                     core.clj:   64
                                                                            ...                                   
                                                            clojure.core/map/fn                     core.clj: 2746
                                                               clojure.core/seq                     core.clj:  137
                                                                            ...                                   
                                                     clojure.core/take-while/fn                     core.clj: 2904
                                                               clojure.core/seq                     core.clj:  137
                                                                            ...                                   
                                                     clojure.core/repeatedly/fn                     core.clj: 5149
                         beat-link-trigger.expressions/build-user-expression/fn              expressions.clj:  411
                                                      clojure.tools.reader/read                   reader.clj:  982
                                                     clojure.tools.reader/read*                   reader.clj:  924
                                                 clojure.tools.reader/read-list                   reader.clj:  208
                                            clojure.tools.reader/read-delimited                   reader.clj:  201
                           clojure.tools.reader.impl.errors/throw-eof-delimited                   errors.clj:   57
                                                                            ...                                   
                                     clojure.tools.reader.impl.errors/eof-error                   errors.clj:   42
                                     clojure.tools.reader.impl.errors/eof-error                   errors.clj:   46
                                                                            ...                                   
                                      clojure.tools.reader.impl.errors/throw-ex                   errors.clj:   24
                                      clojure.tools.reader.impl.errors/throw-ex                   errors.clj:   34
             clojure.lang.ExceptionInfo: Global Setup Expression [line 1, col 29] Unexpected EOF while reading item 3 of list, starting at line 1 and column 1.
        col: 29
    ex-kind: :eof
       file: "Global Setup Expression"
       line: 1
       type: :reader-exception
clojure.lang.Compiler$CompilerException: Syntax error macroexpanding beat-link-trigger.expressions/wrap-user-expression at (0:0).
    data: {:clojure.error/phase :macro-syntax-check,
           :clojure.error/line 0,
           :clojure.error/column 0,
           :clojure.error/source "NO_SOURCE_PATH",
           :clojure.error/symbol
           beat-link-trigger.expressions/wrap-user-expression}
brunchboy commented 3 years ago

My bad, I somehow added an incorrect extra second open parenthesis at the start. Fixing…

brunchboy commented 3 years ago

I can confirm that the same issue happens with my Korg nanoKONTROL2 under Raspbian. So there is some fundamental difference between the way MIDI objects returned by Linux behave when compared to those returned by macOS and Windows. It may not be possible to hold onto long-lived references to them in Linux, but we can’t change the way we do it in macOS and Windows because it’s working well and relied on in many different performance environments. So if we can find any Linux JVM MIDI experts willing to dive into the Clojure to help here, that would be wonderful.

My attempts to open an nREPL connection between Emacs on my macOS development machine and the Beat Link Trigger process running on the Pi have failed so far, which is going to greatly slow down my efforts to research and fix this. It would be best taken on by someone who actually has a reason to use BLT with MIDI under Linux, in any case.

Ideas, experiments, and even PRs that don’t break macOS and Windows MIDI are most welcome!

brunchboy commented 3 years ago

Woo hoo, I think I have an answer! It turns out all this time I’ve been calling the midi-clj functions wrong, because they behave in a way that is not idiomatic Clojure (they are pretty old, they were written when Clojure was new and best practices weren’t widely known). They always treat search strings as if they were regular expressions, and so the brackets in Linux device names are making them not match. The more idiomatic (and developer-friendly) way for the functions to work would be to accept both plain strings (and perform simple substring matching on them, which is how I assumed they worked) or actual regular expression objects (when that flexibility is needed) as the target being searched for. Regular expressions have nice, concise literal syntax in Clojure so they are easy to type, and then it would be easy to work either way depending on the caller’s needs.

I may open a PR with midi-clj about this, although the only backwards-compatible way to do it would be to offer a new function that works this way and deprecates the old one. (While I’m at it, I might suggest they get rid of the midi- prefix on all their function names, that is also not idiomatic, the best practice is just to name the functions for what they do, and let people use namespace aliases to make it clear where they come from.) But that is a project for another day; for now I just needed to fix the places that Beat Link Trigger is calling these functions to properly escape any regular expression special characters, and MIDI output selection appears to be working in Linux! I was able to set up MIDI triggers to flash buttons on my nanoKONTROL after building with the above commit, and now that I pushed it to GitHub, the cross-platform überjar is ready for you to try as well, @whoman321. Could you download the latest preview jar, and see if it works better for you too?

whoman321 commented 3 years ago

It works!!

Thank you so much for all your work on this.
You're a gem in the development community for sure! I def owe you a few beers some day (when things get back to normal, of coarse).

Not sure if I should close this now or if you want to keep it open for the PR with midi-clj, so I'll leave it to you. Thanks again bro!

brunchboy commented 3 years ago

Awesome! So glad to hear it. And sorry it took so long to set up the Pi to track this down. Thanks for your patience, and all your great analysis. I would definitely love to sit down for a beer someday once we can do those kinds of things again. 😁 What part of the world do you live in?

whoman321 commented 3 years ago

I'm in Los Angeles. You? I travel quite a bit (except this year) so I'm sure our paths may cross at some point.

brunchboy commented 3 years ago

I’m in Madison, WI. Most of the people I hear from seem to be in Europe, for whatever reason. I used to travel a fair bit (for pleasure) and hope to get back to it. And I have a friend who moved from NY to LA, and I have owed her a visit for quite some time.

whoman321 commented 3 years ago

Oh nice! When that time comes definitely message me and we'll do it.