1 /* 2 * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.ByteArrayOutputStream; 25 26 import javax.sound.midi.MidiDevice; 27 import javax.sound.midi.MidiMessage; 28 import javax.sound.midi.MidiSystem; 29 import javax.sound.midi.Receiver; 30 import javax.sound.midi.ShortMessage; 31 import javax.sound.midi.SysexMessage; 32 import javax.sound.midi.Transmitter; 33 34 /** 35 * @test 36 * @bug 4782924 37 * @bug 4812168 38 * @bug 4356787 39 * @summary MIDI i/o. This is an interactive test! Start it and follow the 40 * instructions. 41 * @run main/manual IOLoop 42 */ 43 public class IOLoop { 44 private static final int LONG_SYSEX_LENGTH = 2000; 45 46 private static Receiver receiver; 47 private static Transmitter transmitter; 48 private static MidiMessage receivedMessage; 49 private static ByteArrayOutputStream baos; 50 private static int expectedBytes; 51 private static int receivedBytes; 52 private static Object lock = new Object(); 53 private static long lastTimestamp; 54 55 public static void main(String[] args) throws Exception { 56 ShortMessage sMsg = new ShortMessage(); 57 SysexMessage syMsg = new SysexMessage(); 58 boolean isTestPassed = true; 59 boolean sysExTestPassed = true; 60 boolean isTestExecuted = true; 61 62 out("To run this test successfully, you need to have attached"); 63 out(" your MIDI out port with the MIDI in port."); 64 65 MidiDevice inDev = null; 66 MidiDevice outDev = null; 67 68 // setup 69 try { 70 MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo(); 71 72 int devNum = Integer.decode(args[0]).intValue(); 73 out("-> opening Transmitter from "+infos[devNum]); 74 inDev = MidiSystem.getMidiDevice(infos[devNum]); 75 inDev.open(); 76 transmitter = inDev.getTransmitter(); 77 Receiver testReceiver = new TestReceiver(); 78 transmitter.setReceiver(testReceiver); 79 80 devNum = Integer.decode(args[1]).intValue(); 81 out("-> opening Receiver from "+infos[devNum]); 82 outDev = MidiSystem.getMidiDevice(infos[devNum]); 83 outDev.open(); 84 receiver = outDev.getReceiver(); 85 86 } catch (Exception e) { 87 System.out.println(e); 88 System.out.println("Cannot test!"); 89 return; 90 } 91 92 // test 93 sMsg.setMessage(ShortMessage.NOTE_OFF | 0, 27, 100); 94 isTestPassed &= testMessage(sMsg); 95 sMsg.setMessage(ShortMessage.NOTE_OFF | 0, 0, 0); 96 isTestPassed &= testMessage(sMsg); 97 sMsg.setMessage(ShortMessage.NOTE_OFF | 15, 127, 127); 98 isTestPassed &= testMessage(sMsg); 99 sMsg.setMessage(ShortMessage.NOTE_ON | 4, 27, 0); 100 isTestPassed &= testMessage(sMsg); 101 sMsg.setMessage(ShortMessage.NOTE_ON | 0, 0, 0); 102 isTestPassed &= testMessage(sMsg); 103 sMsg.setMessage(ShortMessage.NOTE_ON | 15, 127, 127); 104 isTestPassed &= testMessage(sMsg); 105 sMsg.setMessage(ShortMessage.POLY_PRESSURE | 11, 98, 99); 106 isTestPassed &= testMessage(sMsg); 107 sMsg.setMessage(ShortMessage.POLY_PRESSURE | 0, 0, 0); 108 isTestPassed &= testMessage(sMsg); 109 sMsg.setMessage(ShortMessage.POLY_PRESSURE | 15, 127, 127); 110 isTestPassed &= testMessage(sMsg); 111 sMsg.setMessage(ShortMessage.CONTROL_CHANGE | 13, 1, 63); 112 isTestPassed &= testMessage(sMsg); 113 sMsg.setMessage(ShortMessage.CONTROL_CHANGE | 0, 0, 0); 114 isTestPassed &= testMessage(sMsg); 115 sMsg.setMessage(ShortMessage.CONTROL_CHANGE | 15, 127, 127); 116 isTestPassed &= testMessage(sMsg); 117 sMsg.setMessage(ShortMessage.PROGRAM_CHANGE | 2, 120, 0); 118 isTestPassed &= testMessage(sMsg); 119 sMsg.setMessage(ShortMessage.PROGRAM_CHANGE | 0, 0, 0); 120 isTestPassed &= testMessage(sMsg); 121 sMsg.setMessage(ShortMessage.PROGRAM_CHANGE | 15, 127, 0); 122 isTestPassed &= testMessage(sMsg); 123 sMsg.setMessage(ShortMessage.CHANNEL_PRESSURE | 6, 30, 0); 124 isTestPassed &= testMessage(sMsg); 125 sMsg.setMessage(ShortMessage.CHANNEL_PRESSURE | 0, 0, 0); 126 isTestPassed &= testMessage(sMsg); 127 sMsg.setMessage(ShortMessage.CHANNEL_PRESSURE | 15, 127, 0); 128 isTestPassed &= testMessage(sMsg); 129 130 sMsg.setMessage(ShortMessage.PITCH_BEND | 6, 56, 4); 131 isTestPassed &= testMessage(sMsg); 132 sMsg.setMessage(ShortMessage.PITCH_BEND | 0, 0, 0); 133 isTestPassed &= testMessage(sMsg); 134 sMsg.setMessage(ShortMessage.PITCH_BEND | 15, 127, 127); 135 isTestPassed &= testMessage(sMsg); 136 137 sMsg.setMessage(ShortMessage.MIDI_TIME_CODE, 0, 0); 138 isTestPassed &= testMessage(sMsg); 139 sMsg.setMessage(ShortMessage.MIDI_TIME_CODE, 127, 0); 140 isTestPassed &= testMessage(sMsg); 141 sMsg.setMessage(ShortMessage.SONG_POSITION_POINTER, 1, 77); 142 isTestPassed &= testMessage(sMsg); 143 sMsg.setMessage(ShortMessage.SONG_POSITION_POINTER, 0, 0); 144 isTestPassed &= testMessage(sMsg); 145 sMsg.setMessage(ShortMessage.SONG_POSITION_POINTER, 127, 127); 146 isTestPassed &= testMessage(sMsg); 147 sMsg.setMessage(ShortMessage.SONG_SELECT, 51, 0); 148 isTestPassed &= testMessage(sMsg); 149 sMsg.setMessage(ShortMessage.SONG_SELECT, 0, 0); 150 isTestPassed &= testMessage(sMsg); 151 sMsg.setMessage(ShortMessage.SONG_SELECT, 127, 0); 152 isTestPassed &= testMessage(sMsg); 153 sMsg.setMessage(ShortMessage.TUNE_REQUEST); 154 isTestPassed &= testMessage(sMsg); 155 156 sMsg.setMessage(ShortMessage.TIMING_CLOCK); 157 isTestPassed &= testMessage(sMsg); 158 sMsg.setMessage(ShortMessage.START); 159 isTestPassed &= testMessage(sMsg); 160 sMsg.setMessage(ShortMessage.CONTINUE); 161 isTestPassed &= testMessage(sMsg); 162 sMsg.setMessage(ShortMessage.STOP); 163 isTestPassed &= testMessage(sMsg); 164 sMsg.setMessage(ShortMessage.ACTIVE_SENSING); 165 isTestPassed &= testMessage(sMsg); 166 sMsg.setMessage(ShortMessage.SYSTEM_RESET); 167 isTestPassed &= testMessage(sMsg); 168 169 syMsg.setMessage(new byte[]{(byte) 0xF0, (byte) 0xF7}, 2); 170 isTestPassed &= testMessage(syMsg); 171 syMsg.setMessage(new byte[]{(byte) 0xF0, 0x01, (byte) 0xF7}, 3); 172 isTestPassed &= testMessage(syMsg); 173 syMsg.setMessage(new byte[]{(byte) 0xF0, 0x02, 0x03, (byte) 0xF7}, 4); 174 isTestPassed &= testMessage(syMsg); 175 syMsg.setMessage(new byte[]{(byte) 0xF0, 0x04, 0x05, 0x06, (byte) 0xF7}, 5); 176 isTestPassed &= testMessage(syMsg); 177 178 if (isTestPassed) { 179 byte[] sysexArray = new byte[LONG_SYSEX_LENGTH]; 180 sysexArray[0] = (byte) 0xF0; 181 for (int i = 1; i < sysexArray.length; i++) { 182 sysexArray[i] = (byte) (i % 0x80); 183 } 184 // syMsg.setMessage(new byte[]{(byte) 0xF7, (byte) ShortMessage.START}, 2); 185 // sMsg.setMessage(ShortMessage.START); 186 // isTestPassed &= testMessage(syMsg, sMsg, DEFAULT_SLEEP_INTERVALL); 187 for (int trial = sysexArray.length; trial > 4; trial -= 1234) { 188 sleep(500); 189 sysexArray[trial - 1] = (byte) 0xF7; 190 syMsg.setMessage(sysexArray, trial); 191 sysExTestPassed &= testMessage(syMsg); 192 break; 193 } 194 } 195 196 // cleanup 197 receiver.close(); 198 transmitter.close(); 199 inDev.close(); 200 outDev.close(); 201 202 if (isTestExecuted) { 203 if (isTestPassed && sysExTestPassed) { 204 205 out("Test PASSED."); 206 } else { 207 if (isTestPassed 208 && !sysExTestPassed 209 && (System.getProperty("os.name").startsWith("Windows"))) { 210 out("Some Windows MIDI i/o drivers have a problem with larger "); 211 out("sys ex messages. The failing sys ex cases are OK, therefore."); 212 out("Test PASSED."); 213 } else { 214 throw new Exception("Test FAILED."); 215 } 216 } 217 } else { 218 out("Test NOT FAILED"); 219 } 220 } 221 222 private static boolean testMessage(MidiMessage message) { 223 receivedMessage = null; 224 baos = new ByteArrayOutputStream(); 225 expectedBytes = message.getLength(); 226 receivedBytes = 0; 227 System.out.print("Sending message " + getMessageString(message.getMessage())+"..."); 228 receiver.send(message, -1); 229 /* sending 3 bytes can roughly be done in 1 millisecond, 230 * so this estimate waits at max 3 times longer than the message takes, 231 * plus a little offset to allow the MIDI subsystem some processing time 232 */ 233 int offset = 300; // standard offset 100 millis 234 if (message instanceof SysexMessage) { 235 // add a little processing time to sysex messages 236 offset += 1000; 237 } 238 if (receivedBytes < expectedBytes) { 239 sleep(expectedBytes + offset); 240 } 241 boolean equal; 242 byte[] data = baos.toByteArray(); 243 if (data.length > 0) { 244 equal = messagesEqual(message.getMessage(), data); 245 } else { 246 equal = messagesEqual(message, receivedMessage); 247 if (receivedMessage != null) { 248 data = receivedMessage.getMessage(); 249 } else { 250 data = null; 251 } 252 } 253 if (!equal) { 254 if ((message.getStatus() & 0xF0) == ShortMessage.PITCH_BEND) { 255 out("NOT failed (may expose a bug in ALSA)"); 256 equal = true; 257 sleep(100); 258 } 259 if ((message.getStatus() == 0xF6) && (message.getLength() == 1)) { 260 out("NOT failed (may expose an issue on Solaris)"); 261 equal = true; 262 sleep(100); 263 } 264 else if ((message.getStatus()) == 0xF0 && message.getLength() < 4) { 265 out("NOT failed (not a correct sys ex message)"); 266 equal = true; 267 sleep(200); 268 } else { 269 out("FAILED:"); 270 out(" received as " + getMessageString(data)); 271 } 272 } else { 273 System.out.println("OK"); 274 } 275 return equal; 276 } 277 278 private static void sleep(int milliseconds) { 279 synchronized(lock) { 280 try { 281 lock.wait(milliseconds); 282 } catch (InterruptedException e) { 283 } 284 } 285 } 286 287 private static String getMessageString(byte[] data) { 288 String s; 289 if (data == null) { 290 s = "<null>"; 291 } else if (data.length == 0) { 292 s = "0-sized array"; 293 } else { 294 int status = data[0] & 0xFF; 295 if (data.length <= 3) { 296 if (status < 240) { 297 s = "command 0x" + Integer.toHexString(status & 0xF0) + " channel " + (status & 0x0F); 298 } else { 299 s = "status 0x" + Integer.toHexString(status); 300 } 301 if (data.length > 1) { 302 s += " data 0x" + Integer.toHexString(data[1] & 0xFF); 303 if (data.length > 2) { 304 s += " 0x" + Integer.toHexString(data[2] & 0xFF); 305 } 306 } 307 } else { 308 s = "status " + Integer.toHexString(status)+" and length "+data.length+" bytes"; 309 } 310 } 311 return s; 312 } 313 314 private static boolean messagesEqual(MidiMessage m1, MidiMessage m2) { 315 if (m1 == null || m2 == null) { 316 return false; 317 } 318 if (m1.getLength() != m2.getLength()) { 319 return false; 320 } 321 byte[] array1 = m1.getMessage(); 322 byte[] array2 = m2.getMessage(); 323 return messagesEqual(array1, array2); 324 } 325 326 private static boolean messagesEqual(byte[] a1, byte[] a2) { 327 if (a1.length != a2.length) return false; 328 for (int i = 0; i < a1.length; i++) { 329 if (a1[i] != a2[i]) { 330 return false; 331 } 332 } 333 return true; 334 } 335 336 private static void out(String s) { 337 System.out.println(s); 338 System.out.flush(); 339 } 340 341 private static String canIn(MidiDevice dev) { 342 if (dev.getMaxTransmitters() != 0) { 343 return "IN "; 344 } 345 return " "; 346 } 347 348 private static String canOut(MidiDevice dev) { 349 if (dev.getMaxReceivers() != 0) { 350 return "OUT "; 351 } 352 return " "; 353 } 354 355 356 private static void checkTimestamp(long timestamp) { 357 // out("checking timestamp..."); 358 if (timestamp < 1) { 359 out("timestamp 0 or negative!"); 360 } 361 if (timestamp < lastTimestamp) { 362 out("timestamp not progressive!"); 363 } 364 lastTimestamp = timestamp; 365 } 366 367 private static class TestReceiver implements Receiver { 368 public void send(MidiMessage message, long timestamp) { 369 //System.out.print(""+message.getLength()+".."); 370 checkTimestamp(timestamp); 371 try { 372 receivedMessage = message; 373 if (message.getStatus() == 0xF0 374 || (message.getLength() > 3 && message.getStatus() != 0xF7)) { 375 // sys ex message 376 byte[] data = message.getMessage(); 377 baos.write(data); 378 receivedBytes += data.length; 379 } 380 else if (message.getStatus() == 0xF7) { 381 // sys ex cont'd message 382 byte[] data = message.getMessage(); 383 // ignore the prepended 0xF7 384 baos.write(data, 1, data.length-1); 385 receivedBytes += (data.length - 1); 386 } else { 387 receivedBytes += message.getLength(); 388 } 389 if (receivedBytes >= expectedBytes) { 390 synchronized(lock) { 391 lock.notify(); 392 } 393 } 394 System.out.print(""+receivedBytes+".."); 395 396 } catch (Exception e) { 397 e.printStackTrace(); 398 } 399 } 400 401 public void close() { 402 } 403 } 404 }