3789983e89
Reviewed-by: darcy, ihse
405 lines
15 KiB
Java
405 lines
15 KiB
Java
/*
|
|
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
|
import javax.sound.midi.MidiDevice;
|
|
import javax.sound.midi.MidiMessage;
|
|
import javax.sound.midi.MidiSystem;
|
|
import javax.sound.midi.Receiver;
|
|
import javax.sound.midi.ShortMessage;
|
|
import javax.sound.midi.SysexMessage;
|
|
import javax.sound.midi.Transmitter;
|
|
|
|
/**
|
|
* @test
|
|
* @bug 4782924
|
|
* @bug 4812168
|
|
* @bug 4356787
|
|
* @summary MIDI i/o. This is an interactive test! Start it and follow the
|
|
* instructions.
|
|
* @run main/manual IOLoop
|
|
*/
|
|
public class IOLoop {
|
|
private static final int LONG_SYSEX_LENGTH = 2000;
|
|
|
|
private static Receiver receiver;
|
|
private static Transmitter transmitter;
|
|
private static MidiMessage receivedMessage;
|
|
private static ByteArrayOutputStream baos;
|
|
private static int expectedBytes;
|
|
private static int receivedBytes;
|
|
private static Object lock = new Object();
|
|
private static long lastTimestamp;
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
ShortMessage sMsg = new ShortMessage();
|
|
SysexMessage syMsg = new SysexMessage();
|
|
boolean isTestPassed = true;
|
|
boolean sysExTestPassed = true;
|
|
boolean isTestExecuted = true;
|
|
|
|
out("To run this test successfully, you need to have attached");
|
|
out(" your MIDI out port with the MIDI in port.");
|
|
|
|
MidiDevice inDev = null;
|
|
MidiDevice outDev = null;
|
|
|
|
// setup
|
|
try {
|
|
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
|
|
|
|
int devNum = Integer.decode(args[0]).intValue();
|
|
out("-> opening Transmitter from "+infos[devNum]);
|
|
inDev = MidiSystem.getMidiDevice(infos[devNum]);
|
|
inDev.open();
|
|
transmitter = inDev.getTransmitter();
|
|
Receiver testReceiver = new TestReceiver();
|
|
transmitter.setReceiver(testReceiver);
|
|
|
|
devNum = Integer.decode(args[1]).intValue();
|
|
out("-> opening Receiver from "+infos[devNum]);
|
|
outDev = MidiSystem.getMidiDevice(infos[devNum]);
|
|
outDev.open();
|
|
receiver = outDev.getReceiver();
|
|
|
|
} catch (Exception e) {
|
|
System.out.println(e);
|
|
System.out.println("Cannot test!");
|
|
return;
|
|
}
|
|
|
|
// test
|
|
sMsg.setMessage(ShortMessage.NOTE_OFF | 0, 27, 100);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.NOTE_OFF | 0, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.NOTE_OFF | 15, 127, 127);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.NOTE_ON | 4, 27, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.NOTE_ON | 0, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.NOTE_ON | 15, 127, 127);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.POLY_PRESSURE | 11, 98, 99);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.POLY_PRESSURE | 0, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.POLY_PRESSURE | 15, 127, 127);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.CONTROL_CHANGE | 13, 1, 63);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.CONTROL_CHANGE | 0, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.CONTROL_CHANGE | 15, 127, 127);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.PROGRAM_CHANGE | 2, 120, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.PROGRAM_CHANGE | 0, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.PROGRAM_CHANGE | 15, 127, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.CHANNEL_PRESSURE | 6, 30, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.CHANNEL_PRESSURE | 0, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.CHANNEL_PRESSURE | 15, 127, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
|
|
sMsg.setMessage(ShortMessage.PITCH_BEND | 6, 56, 4);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.PITCH_BEND | 0, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.PITCH_BEND | 15, 127, 127);
|
|
isTestPassed &= testMessage(sMsg);
|
|
|
|
sMsg.setMessage(ShortMessage.MIDI_TIME_CODE, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.MIDI_TIME_CODE, 127, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.SONG_POSITION_POINTER, 1, 77);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.SONG_POSITION_POINTER, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.SONG_POSITION_POINTER, 127, 127);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.SONG_SELECT, 51, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.SONG_SELECT, 0, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.SONG_SELECT, 127, 0);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.TUNE_REQUEST);
|
|
isTestPassed &= testMessage(sMsg);
|
|
|
|
sMsg.setMessage(ShortMessage.TIMING_CLOCK);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.START);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.CONTINUE);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.STOP);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.ACTIVE_SENSING);
|
|
isTestPassed &= testMessage(sMsg);
|
|
sMsg.setMessage(ShortMessage.SYSTEM_RESET);
|
|
isTestPassed &= testMessage(sMsg);
|
|
|
|
syMsg.setMessage(new byte[]{(byte) 0xF0, (byte) 0xF7}, 2);
|
|
isTestPassed &= testMessage(syMsg);
|
|
syMsg.setMessage(new byte[]{(byte) 0xF0, 0x01, (byte) 0xF7}, 3);
|
|
isTestPassed &= testMessage(syMsg);
|
|
syMsg.setMessage(new byte[]{(byte) 0xF0, 0x02, 0x03, (byte) 0xF7}, 4);
|
|
isTestPassed &= testMessage(syMsg);
|
|
syMsg.setMessage(new byte[]{(byte) 0xF0, 0x04, 0x05, 0x06, (byte) 0xF7}, 5);
|
|
isTestPassed &= testMessage(syMsg);
|
|
|
|
if (isTestPassed) {
|
|
byte[] sysexArray = new byte[LONG_SYSEX_LENGTH];
|
|
sysexArray[0] = (byte) 0xF0;
|
|
for (int i = 1; i < sysexArray.length; i++) {
|
|
sysexArray[i] = (byte) (i % 0x80);
|
|
}
|
|
// syMsg.setMessage(new byte[]{(byte) 0xF7, (byte) ShortMessage.START}, 2);
|
|
// sMsg.setMessage(ShortMessage.START);
|
|
// isTestPassed &= testMessage(syMsg, sMsg, DEFAULT_SLEEP_INTERVALL);
|
|
for (int trial = sysexArray.length; trial > 4; trial -= 1234) {
|
|
sleep(500);
|
|
sysexArray[trial - 1] = (byte) 0xF7;
|
|
syMsg.setMessage(sysexArray, trial);
|
|
sysExTestPassed &= testMessage(syMsg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// cleanup
|
|
receiver.close();
|
|
transmitter.close();
|
|
inDev.close();
|
|
outDev.close();
|
|
|
|
if (isTestExecuted) {
|
|
if (isTestPassed && sysExTestPassed) {
|
|
|
|
out("Test PASSED.");
|
|
} else {
|
|
if (isTestPassed
|
|
&& !sysExTestPassed
|
|
&& (System.getProperty("os.name").startsWith("Windows"))) {
|
|
out("Some Windows MIDI i/o drivers have a problem with larger ");
|
|
out("sys ex messages. The failing sys ex cases are OK, therefore.");
|
|
out("Test PASSED.");
|
|
} else {
|
|
throw new Exception("Test FAILED.");
|
|
}
|
|
}
|
|
} else {
|
|
out("Test NOT FAILED");
|
|
}
|
|
}
|
|
|
|
private static boolean testMessage(MidiMessage message) {
|
|
receivedMessage = null;
|
|
baos = new ByteArrayOutputStream();
|
|
expectedBytes = message.getLength();
|
|
receivedBytes = 0;
|
|
System.out.print("Sending message " + getMessageString(message.getMessage())+"...");
|
|
receiver.send(message, -1);
|
|
/* sending 3 bytes can roughly be done in 1 millisecond,
|
|
* so this estimate waits at max 3 times longer than the message takes,
|
|
* plus a little offset to allow the MIDI subsystem some processing time
|
|
*/
|
|
int offset = 300; // standard offset 100 millis
|
|
if (message instanceof SysexMessage) {
|
|
// add a little processing time to sysex messages
|
|
offset += 1000;
|
|
}
|
|
if (receivedBytes < expectedBytes) {
|
|
sleep(expectedBytes + offset);
|
|
}
|
|
boolean equal;
|
|
byte[] data = baos.toByteArray();
|
|
if (data.length > 0) {
|
|
equal = messagesEqual(message.getMessage(), data);
|
|
} else {
|
|
equal = messagesEqual(message, receivedMessage);
|
|
if (receivedMessage != null) {
|
|
data = receivedMessage.getMessage();
|
|
} else {
|
|
data = null;
|
|
}
|
|
}
|
|
if (!equal) {
|
|
if ((message.getStatus() & 0xF0) == ShortMessage.PITCH_BEND) {
|
|
out("NOT failed (may expose a bug in ALSA)");
|
|
equal = true;
|
|
sleep(100);
|
|
}
|
|
if ((message.getStatus() == 0xF6) && (message.getLength() == 1)) {
|
|
out("NOT failed (may expose an issue on Solaris)");
|
|
equal = true;
|
|
sleep(100);
|
|
}
|
|
else if ((message.getStatus()) == 0xF0 && message.getLength() < 4) {
|
|
out("NOT failed (not a correct sys ex message)");
|
|
equal = true;
|
|
sleep(200);
|
|
} else {
|
|
out("FAILED:");
|
|
out(" received as " + getMessageString(data));
|
|
}
|
|
} else {
|
|
System.out.println("OK");
|
|
}
|
|
return equal;
|
|
}
|
|
|
|
private static void sleep(int milliseconds) {
|
|
synchronized(lock) {
|
|
try {
|
|
lock.wait(milliseconds);
|
|
} catch (InterruptedException e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
private static String getMessageString(byte[] data) {
|
|
String s;
|
|
if (data == null) {
|
|
s = "<null>";
|
|
} else if (data.length == 0) {
|
|
s = "0-sized array";
|
|
} else {
|
|
int status = data[0] & 0xFF;
|
|
if (data.length <= 3) {
|
|
if (status < 240) {
|
|
s = "command 0x" + Integer.toHexString(status & 0xF0) + " channel " + (status & 0x0F);
|
|
} else {
|
|
s = "status 0x" + Integer.toHexString(status);
|
|
}
|
|
if (data.length > 1) {
|
|
s += " data 0x" + Integer.toHexString(data[1] & 0xFF);
|
|
if (data.length > 2) {
|
|
s += " 0x" + Integer.toHexString(data[2] & 0xFF);
|
|
}
|
|
}
|
|
} else {
|
|
s = "status " + Integer.toHexString(status)+" and length "+data.length+" bytes";
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
private static boolean messagesEqual(MidiMessage m1, MidiMessage m2) {
|
|
if (m1 == null || m2 == null) {
|
|
return false;
|
|
}
|
|
if (m1.getLength() != m2.getLength()) {
|
|
return false;
|
|
}
|
|
byte[] array1 = m1.getMessage();
|
|
byte[] array2 = m2.getMessage();
|
|
return messagesEqual(array1, array2);
|
|
}
|
|
|
|
private static boolean messagesEqual(byte[] a1, byte[] a2) {
|
|
if (a1.length != a2.length) return false;
|
|
for (int i = 0; i < a1.length; i++) {
|
|
if (a1[i] != a2[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static void out(String s) {
|
|
System.out.println(s);
|
|
System.out.flush();
|
|
}
|
|
|
|
private static String canIn(MidiDevice dev) {
|
|
if (dev.getMaxTransmitters() != 0) {
|
|
return "IN ";
|
|
}
|
|
return " ";
|
|
}
|
|
|
|
private static String canOut(MidiDevice dev) {
|
|
if (dev.getMaxReceivers() != 0) {
|
|
return "OUT ";
|
|
}
|
|
return " ";
|
|
}
|
|
|
|
|
|
private static void checkTimestamp(long timestamp) {
|
|
// out("checking timestamp...");
|
|
if (timestamp < 1) {
|
|
out("timestamp 0 or negative!");
|
|
}
|
|
if (timestamp < lastTimestamp) {
|
|
out("timestamp not progressive!");
|
|
}
|
|
lastTimestamp = timestamp;
|
|
}
|
|
|
|
private static class TestReceiver implements Receiver {
|
|
public void send(MidiMessage message, long timestamp) {
|
|
//System.out.print(""+message.getLength()+"..");
|
|
checkTimestamp(timestamp);
|
|
try {
|
|
receivedMessage = message;
|
|
if (message.getStatus() == 0xF0
|
|
|| (message.getLength() > 3 && message.getStatus() != 0xF7)) {
|
|
// sys ex message
|
|
byte[] data = message.getMessage();
|
|
baos.write(data);
|
|
receivedBytes += data.length;
|
|
}
|
|
else if (message.getStatus() == 0xF7) {
|
|
// sys ex cont'd message
|
|
byte[] data = message.getMessage();
|
|
// ignore the prepended 0xF7
|
|
baos.write(data, 1, data.length-1);
|
|
receivedBytes += (data.length - 1);
|
|
} else {
|
|
receivedBytes += message.getLength();
|
|
}
|
|
if (receivedBytes >= expectedBytes) {
|
|
synchronized(lock) {
|
|
lock.notify();
|
|
}
|
|
}
|
|
System.out.print(""+receivedBytes+"..");
|
|
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
}
|
|
}
|
|
}
|