/* CSC220 & CSC480 students fall 2021 IF YOU HAVE BEEN HAVING PROBLEMS WITH THE CALL TO initMIDI() TIMING OUT AND BLOWING UP PROCESSING WITH THIS ERROR: RuntimeException: Waited 5000ms for: [count 2, qsz 0, owner THEN GO INTO THE MIDI TAB AND REPLACE THE CODE THAT STARTS LIKE THIS: // MIDI VARIABLES, see http://faculty.kutztown.edu/parson/spring2017/MidiKBSpring2016Parson.txt final int midiDeviceIndex = 0 ; // setup() checks for number of devices. Use one for output. DOWN THROUGH THE BOTTOM OF THE MIDI TAB INCLUDING THE sendMIDI() FUNCTION WITH THE CODE BELOW. IF IT HAS BEEN WORKING FOR YOU, DON'T CHANGE THIS MIDI TAB CODE. */ // START OF CONCURRENT initMIDI() & sendMIDI() import java.util.concurrent.* ; final MidiRunner runner = new MidiRunner(); void initMIDI() { Thread thr = new Thread(runner); thr.start(); } class MIDImsg { final int cmd, chan, data1, data2 ; MIDImsg(int cmd, int chan, int data1, int data2) { this.cmd = cmd ; this.chan = chan ; this.data1 = data1 ; this.data2 = data2 ; } } class MidiRunner implements java.lang.Runnable { // TODO THREADS Move this call back to the bottom of setup(). // MOVE ALL THE CODE FROM HERE THROUGH sendMIDI() INTO AN ACTIVE HELPER CLASS final LinkedBlockingQueue inq = new LinkedBlockingQueue() ; // accepts msgs from Processing // MIDI VARIABLES, see http://faculty.kutztown.edu/parson/spring2017/MidiKBSpring2016Parson.txt final int midiDeviceIndex = 0 ; // setup() checks for number of devices. Use one for output. // NOTE: A final variable is in fact a constant that cannot be changed. void run() { MidiDevice.Info[] midiDeviceInfo = null ; // See javax.sound.midi.MidiSystem and javax.sound.midi.MidiDevice MidiDevice device = null ; // See javax.sound.midi.MidiSystem and javax.sound.midi.MidiDevice Receiver receiver = null ; // javax.sound.midi.Receiver receives your OUTPUT MIDI messages (counterintuitive?) // SEE https://www.midi.org/specifications/item/gm-level-1-sound-set but start at 0, not 1 // MIDI: // 1. FIND OUT WHAT MIDI DEVICES ARE AVAILABLE FOR VARIABLE midiDeviceIndex. midiDeviceInfo = MidiSystem.getMidiDeviceInfo(); for (int i = 0 ; i < midiDeviceInfo.length ; i++) { println("MIDI DEVICE NUMBER " + i + " Name: " + midiDeviceInfo[i].getName() + ", Vendor: " + midiDeviceInfo[i].getVendor() + ", Description: " + midiDeviceInfo[i].getDescription()); } // 2. OPEN ONE OF THE MIDI DEVICES UP FOR OUTPUT. try { device = MidiSystem.getMidiDevice(midiDeviceInfo[midiDeviceIndex]); device.open(); // Make sure to close it before this sketch terminates!!! // There should be a way to schedule a method when Processing closes this // sketch, so we can close the device there, but it is not documented for Processing 3. receiver = device.getReceiver(); // NOTE: Either of the above method calls can throw MidiUnavailableException // if there is no available device or if it does not have a Receiver to // which we can send messages. The catch clause intercepts those error messages. // See https://www.midi.org/specifications/item/gm-level-1-sound-set, use patch variable /* NOT USED IN THIS SKETCH: ShortMessage noteMessage1 = new ShortMessage() ; noteMessage1.setMessage(ShortMessage.PROGRAM_CHANGE, 0, patch, 0); // to channel 0 receiver.send(noteMessage1, -1L); // send it now ShortMessage noteMessage2 = new ShortMessage() ; noteMessage2.setMessage(ShortMessage.PROGRAM_CHANGE, 1, (patch+32)%128, 0); // to channel 1 receiver.send(noteMessage2, -1L); // send it now */ } catch (MidiUnavailableException mx) { System.err.println("MIDI UNAVAILABLE"); // Error messages go here. device = null ; receiver = null ; // Do not try to use them. return ; /* } catch (InvalidMidiDataException dx) { System.err.println("MIDI ERROR: " + dx.getMessage()); // Error messages go here. */ } int volume = 7 ; int volumeLevel = 32 ; for (int c = 0 ; c < 16 ; c++) { sendMIDI(ShortMessage.CONTROL_CHANGE, c, volume, volumeLevel); } while (true) { // START THE SERVICE THREAD MIDImsg msg ; try { msg = inq.take(); } catch (InterruptedException tx) { msg = null ; println("INTERRUPTED!"); } if (msg != null) { try { ShortMessage message = new ShortMessage(msg.cmd, msg.chan, msg.data1, msg.data2); receiver.send(message, -1L); // -1L means send it now, not queued for later. msg = null ; } catch (InvalidMidiDataException ix) { System.err.println("Thread InvalidMidiDataException: " + ix.getMessage()); } } } } } void sendMIDI(int command, int channel, int data1, int data2) { // Construct & send a ShortMessage with these parameters. if (channel >= 0 && channel < 16) { // channel -1 in this sketch used to mean "not sounding" MIDImsg msg = new MIDImsg(command, channel, data1, data2); runner.inq.offer(msg); } }