5

I've been banging my head on this all day, read everything I can find, followed the JDK source around, no luck in finding out the gory details about HOW or WHERE java looks to obtain data on a midi device and determines what's what.

I'm trying to capture midi messages through my NI Audio 8 DJ MIDI IN port, but, java isn't "seeing" the MIDI IN port, only the out, which I have successfully used to send midi with. I also get the same results with an M-Audio USB UNO midi device: MidiSystem.getMidiDeviceInfo() only "sees" the output port.

I have verified the operation of the MIDI IN port with:

amidi -p hw:2,0 -d

and sending it some signals. Works fine.

getMaxTransmitters() returns zero. MidiSystem.getMidiDeviceInfo() shows only one entry for both devices: Audio8DJ [hw:2,0] or Interface [hw:2,0]

The code below works fine for a Receiver and I think is only the bits I need to verify that getTransmitter() grabs the port, since it just works for the other and everything works fine, up I get a MidiUnavailableException / Transmitter not available exception.

I've even taken the getMaxReceivers() trap out because I was just trying to see if the device only offered up the one entry and sorted it out, but no.

public static Transmitter getMidiIn () {
    if (midiIn == null){
        devices = MidiSystem.getMidiDeviceInfo();
        for(MidiDevice.Info info: devices){
            System.out.println("device class " + info.getClass());
            MidiDevice device;
            try{
                device = MidiSystem.getMidiDevice(info);
                if (info.toString().equals("Audio8DJ [hw:2,0]")){

                    if (device.getMaxTransmitters() != 0){
                        try{
                            device.open();
                            System.out.println("max transmitters:" + device.getMaxTransmitters());
                            midiIn = device.getTransmitter();
                            System.out.println("Found a transmitter: "+ midiIn);
                            break;
                        } catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            } catch (MidiUnavailableException e1){
                e1.printStackTrace();

            }
        }
    }
    return midiIn;
}

So the thing that's getting me here is this: alsa lists only one entry in amidi -l and when I specify that as a port to dump, it works fine. Java gets that same text entry and can't sort out the MIDI IN, assigning it the com.sun.media.sound.MidiOutDeviceProvider class so it leaves me wondering just how does, or where does Java figure out what a device has to offer and why isn't it seeing the input port that alsa is seeing.

I'm coding with eclipse Version: 3.8.1 IDE with JDK1.6, on a linux mint OS, .

I'll be happy to provide anything asked for. Thanks for reading!

TT.
  • 15,774
  • 6
  • 47
  • 88
  • 2
    Just a random thought... have you tried running it using `sudo`? (i.e., `sudo java your.class.name` at the command line.) I've seen issues where you need to be root to manipulate some devices, so it might be worth a try. – Mike Harris Jan 06 '16 at 17:59
  • 1
    I've thought of that, and I'm open to suggestion, but, I've been hesitant to try for two reasons surrounding the knee-jerk reaction that running complex applications as root is dangerous: 1) since it can obtain the MIDI OUT port with out issue, then I'd rather find out what file(s) java is accessing and fix the problem there. 2) it should not be run as root and if running as root solves it, I'm back to the same problem of figuring out what file has the access problem. – Daniel Sevelt Jan 07 '16 at 17:55
  • 1
    Thanks again for the help Mike, you got me headed in the right direction. Please see my answer below. – Daniel Sevelt Feb 02 '16 at 14:02

2 Answers2

3

The solution to java seeing the transmitter was in the version of JDK being used, though unfortunately at this time, I do not have an answer as to why for the failure, just that one version worked and suits my needs for the time being. If I find that answer, I will edit this answer.

Of the three versions I was switching between for testing, jdk1.8.0_60, jdk1.7.0_80, jdk1.6.0_45, 1.7 did not experience the error and successfully obtained a transmitter from my device. I found this out, and that privileges were not the cause of my specific issue, by compiling and running some code I found suitable for command line execution that attempts to obtain the transmitter, prints out midi data sent to it, and modified it a little ...

import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;

public class MidiInputTest {

    public MidiDevice       input;
    public MidiDevice       output;

    public static void main(String[] args) {
        new MidiInputTest().start();
    } 


    public void start() {
        init();  // initialize your midi input device
                 // system dependent


        try {
            output.open(); // From midi device
            MyReceiver myrcvr = new MyReceiver();
            MidiSystem.getTransmitter().setReceiver(myrcvr);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(0);
        }

    }

    private class MyReceiver implements Receiver  {
        Receiver rcvr;
        public MyReceiver() {
            try {
                this.rcvr = MidiSystem.getReceiver();
            } catch (MidiUnavailableException mue) {
                mue.printStackTrace();
            }
        }

        @Override
        public void send(MidiMessage message, long timeStamp) {
            byte[] b = message.getMessage();
            if (b[0] != (byte)254) {
                System.out.println((b[0] & 0xff) + " " + (b[1] & 0xff));
            }
            //rcvr.send(message, timeStamp); // will send out what ever you receive
        }

        @Override
        public void close() {
            rcvr.close();
        }
    } 
    public void init() {

        MidiDevice.Info[] devices;

        devices = MidiSystem.getMidiDeviceInfo();
        try{

            for (MidiDevice.Info info: devices) {
                MidiDevice device;
                device = MidiSystem.getMidiDevice(info);
                System.out.println("MidiDevice.Info="+info + "\n" + "maxTransmitters="+device.getMaxTransmitters());

                // I put the specific device I want to connect to behind an if gate here to avoid connecting to something I do not

                if (info.toString().equals("Interface [hw:2,0,0]") && device.getMaxTransmitters() != 0) { 
                    System.out.println(" Name: " + info.toString() +
                            ", Decription: " +
                            info.getDescription() +
                            ", Vendor: " +
                            info.getVendor());

                    output = MidiSystem.getMidiDevice(info);

                   if (! output.isOpen()) {
                        output.open();
                   }
                }
            }
        } catch (MidiUnavailableException mue) {
                    mue.printStackTrace();
        }
    }
}

To run this from the command line choose a version of JDK you have installed, compile and run it with those specific versions substituting jdk1.7.0_80 for the distro you wish to test.

/opt/java-jdk/jdk1.7.0_80/bin/javac MidiInputTest.java
/opt/java-jdk/jdk1.7.0_80/bin/java -cp . MidiInputTest

Though I haven't been able to verify it, Java Sound is apparently responsible for figuring out what is available for java's use from your MIDI architecture.

Thank you Mike Harris for sending me down the right path of testing on the command line, and jim829 over at java-forums.org for the example code for the command line.

  • 1
    JDK 1.7 is no longer available on Ubuntu 16.04 and later... it can be installed through a manual process, but is not secure. Is it worth reporting a Java bug? – nyanpasu64 May 17 '19 at 13:05
0

I had this problem which was caused by multiple instances of device info with the same name reterned by MidiSystem.getMidiDeviceInfo(). Basically when you call MidiSystem.getMidiDevice(Info) if you're unlucky it'll return the one without the transmitter. I'm not sure why it does this but only one instance has transmitters, (I think one might be for in and one out but, not sure). By first getting just the devices that have transmitters and then selecting from these, with the desired info, it worked for me. Hope that helps

public ArrayList<MidiDevice> getTransmitterDevices() {
    MidiDevice.Info[] deviceInfo = MidiSystem.getMidiDeviceInfo();

    ArrayList<MidiDevice> transmitterDevices = new ArrayList<>();
        for(int i=0;i<deviceInfo.length;i++) {
            try {
                MidiDevice device = MidiSystem.getMidiDevice(deviceInfo[i]);
                if(device.getMaxTransmitters()!=0) {
                    transmitterDevices.add(device);
                }
            } catch (MidiUnavailableException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    return transmitterDevices;
}
//Somewhere else 
//choose appropriate info somehow
MidiDevices<ArrayList> transmitterDevices = getTransmitterDevices();
    for(MidiDevice tmp : transmitterDevices) {
        if(tmp.getDeviceInfo().equals(info)) {
            try {
                midiController = tmp;
                Transmitter transmitter = midiController.getTransmitter();
                // something that implements receiver
                midiReceiver = new MidiReceiver(); 
                transmitter.setReceiver(midiReceiver);
                midiController.open();
                System.out.println("controller set ok");
            } catch (MidiUnavailableException e) {
                // TODO Auto-generated catch block
                    e.printStackTrace();
            }
       }
}