Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

rwmidi is not thread-safe #1

Open
wants to merge 7 commits into from

2 participants

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
6 .gitignore
@@ -0,0 +1,6 @@
+/.classpath
+
+/.project
+
+bin/
+.gitgnore
View
13 src/rwdoclet/src/com/ruinwesen/doclet/RWDoclet.java
@@ -3,12 +3,17 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
-import java.util.regex.*;
-
-import com.sun.javadoc.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import com.sun.javadoc.ClassDoc;
+import com.sun.javadoc.ConstructorDoc;
+import com.sun.javadoc.ExecutableMemberDoc;
+import com.sun.javadoc.MethodDoc;
+import com.sun.javadoc.Parameter;
+import com.sun.javadoc.RootDoc;
+import com.sun.javadoc.Tag;
public class RWDoclet {
static String outputDir = "/Users/manuel/javadoc-output/";
View
214 src/rwmidi/MidiEvent.java
@@ -1,105 +1,109 @@
-package rwmidi;
-
-import javax.sound.midi.InvalidMidiDataException;
-import javax.sound.midi.MidiMessage;
-import javax.sound.midi.ShortMessage;
-
-/**
- * Simple wrapper around MIDI messages, used to abstract from the actual bytes and provide a
- * more symbolic representation of the MIDI data. This class is used as a superclass for
- * messages received on a {@Link MidiInput} object. You don't usually have to create such objects yourself.
- *
- */
-public class MidiEvent extends ShortMessage{
- public static final int SYSEX_START = 0xF0;
- public static final int SYSEX_END = 0xF7;
- public static final int NOTE_OFF = 0x80;
- public static final int NOTE_ON = 0x90;
- public static final int CONTROL_CHANGE = 0xB0;
- public static final int PROGRAM_CHANGE = 0xC0;
- private int midiChannel = 0;
-
- MidiInput input = null;
-
- protected MidiEvent(byte[] data){
- super(data);
- }
-
- MidiEvent(final MidiMessage _midiMessage){
- this(_midiMessage.getMessage());
- }
-
- MidiEvent(int command, int number, int value){
- this(new byte[] { (byte) NOTE_ON, 0, 0 });
- try{
- setMessage(command | midiChannel, number, value);
- }catch (InvalidMidiDataException e){
- e.printStackTrace();
- }
- }
-
- /**
- *
- * @return the input on which this message was received.
- */
- public MidiInput getInput() {
- return input;
- }
-
- void setInput(MidiInput _input) {
- input = _input;
- }
-
- /**
- *
- * @return the first data byte of this message
- */
- public int getData1(){
- if (length > 1){
- return (data[1] & 0xFF);
- }
- return 0;
- }
-
- void setData1(final int _data1){
- data[1] = (byte) (_data1 & 0xFF);
- }
-
- /**
- *
- * @return the second data byte of this message
- */
- public int getData2(){
- if (length > 2){
- return (data[2] & 0xFF);
- }
- return 0;
- }
-
- void setData2(final int _data2){
- data[1] = (byte) (_data2 & 0xFF);
- }
-
- protected static MidiEvent create(MidiMessage msg) {
- if (msg instanceof javax.sound.midi.SysexMessage)
- return new SysexMessage((javax.sound.midi.SysexMessage)msg);
- else if (msg instanceof ShortMessage) {
- ShortMessage smsg = (ShortMessage)msg;
- final int midiCommand = smsg.getCommand();
- final int midiChannel = smsg.getChannel();
- final int midiData1 = smsg.getData1();
- final int midiData2 = smsg.getData2();
-
- if (midiCommand == MidiEvent.NOTE_ON && midiData2 > 0) {
- return new Note(midiCommand, midiChannel, midiData1, midiData2);
- } else if (midiCommand == MidiEvent.NOTE_OFF || ((midiCommand == NOTE_ON) && (midiData2 == 0))) {
- return new Note(midiCommand, midiChannel, midiData1, midiData2);
- } else if (midiCommand == MidiEvent.CONTROL_CHANGE) {
- return new Controller(midiChannel, midiData1, midiData2);
- } else if (midiCommand == MidiEvent.PROGRAM_CHANGE) {
- return new ProgramChange(midiData1);
- }
- }
- return null;
- }
-}
+package rwmidi;
+
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MidiMessage;
+import javax.sound.midi.ShortMessage;
+
+/**
+ * Simple wrapper around MIDI messages, used to abstract from the actual bytes and provide a
+ * more symbolic representation of the MIDI data. This class is used as a superclass for
+ * messages received on a {@Link MidiInput} object. You don't usually have to create such objects yourself.
+ *
+ */
+public class MidiEvent extends ShortMessage{
+ public static final int SYSEX_START = 0xF0;
+ public static final int SYSEX_END = 0xF7;
+ public static final int NOTE_OFF = 0x80;
+ public static final int NOTE_ON = 0x90;
+ public static final int CONTROL_CHANGE = 0xB0;
+ public static final int PITCH_BEND = 0xE0;
+
+ private int midiChannel = 0;
+
+ MidiInput input = null;
+
+ protected MidiEvent(byte[] data){
+ super(data);
+ }
+
+ MidiEvent(final MidiMessage _midiMessage){
+ this(_midiMessage.getMessage());
+ }
+
+ MidiEvent(int command, int number, int value){
+ this(new byte[] { (byte) NOTE_ON, 0, 0 });
+ try{
+ setMessage(command | midiChannel, number, value);
+ }catch (InvalidMidiDataException e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ *
+ * @return the input on which this message was received.
+ */
+ public MidiInput getInput() {
+ return input;
+ }
+
+ void setInput(MidiInput _input) {
+ input = _input;
+ }
+
+ /**
+ *
+ * @return the first data byte of this message
+ */
+ public int getData1(){
+ if (length > 1){
+ return (data[1] & 0xFF);
+ }
+ return 0;
+ }
+
+ void setData1(final int _data1){
+ data[1] = (byte) (_data1 & 0xFF);
+ }
+
+ /**
+ *
+ * @return the second data byte of this message
+ */
+ public int getData2(){
+ if (length > 2){
+ return (data[2] & 0xFF);
+ }
+ return 0;
+ }
+
+ void setData2(final int _data2){
+ data[1] = (byte) (_data2 & 0xFF);
+ }
+
+ protected static MidiEvent create(MidiMessage msg) {
+ if (msg instanceof javax.sound.midi.SysexMessage)
+ return new SysexMessage((javax.sound.midi.SysexMessage)msg);
+ else if (msg instanceof ShortMessage) {
+ ShortMessage smsg = (ShortMessage)msg;
+ final int midiCommand = smsg.getCommand();
+ final int sysMessage = smsg.getStatus();
+ final int midiChannel = smsg.getChannel();
+ final int midiData1 = smsg.getData1();
+ final int midiData2 = smsg.getData2();
+
+ if (midiCommand == MidiEvent.NOTE_ON && midiData2 > 0) {
+ return new Note(midiCommand, midiChannel, midiData1, midiData2);
+ } else if (midiCommand == MidiEvent.NOTE_OFF || ((midiCommand == NOTE_ON) && (midiData2 == 0))) {
+ return new Note(midiCommand, midiChannel, midiData1, midiData2);
+ } else if (midiCommand == MidiEvent.CONTROL_CHANGE) {
+ return new Controller(midiChannel, midiData1, midiData2);
+ } else if (midiCommand == MidiEvent.PROGRAM_CHANGE) {
+ return new ProgramChange(midiData1);
+ } else if (sysMessage >= MidiEvent.SONG_POSITION_POINTER) { //if we get this far, only syncs are left
+ return new SyncEvent(msg);
+ }
+ }
+ return null;
+ }
+}
View
136 src/rwmidi/MidiInput.java 100644 → 100755
@@ -1,14 +1,15 @@
package rwmidi;
+import java.util.List;
import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiMessage;
-import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
-import javax.sound.midi.ShortMessage;
-import javax.sound.midi.SysexMessage;
import javax.sound.midi.Transmitter;
/**
@@ -18,9 +19,14 @@
* to close the corresponding MidiDevice (however, this will close all MidiInputs connected to this device).
*/
public class MidiInput implements Receiver {
-
+
javax.sound.midi.MidiDevice jDevice;
- ArrayList<Plug> plugList;
+ final List currentMessage = new ArrayList();
+ final List<Plug> plugList = new CopyOnWriteArrayList<Plug>();
+ int divisions = 0;
+ long pulseOne = 0;
+ long pulseTwo = 0;
+ long pulseTime;
/**
* Create a MidiInput from a javax.sound.midi.MidiDevice . Don't use this unless you know what you are doing.
@@ -29,18 +35,16 @@
*/
public MidiInput(javax.sound.midi.MidiDevice dev2) throws MidiUnavailableException {
this.jDevice = dev2;
- dev2.open();
+ if (!dev2.isOpen())
+ dev2.open();
Transmitter trsmt = dev2.getTransmitter();
trsmt.setReceiver(this);
- plugList = new ArrayList<Plug>();
- currentMessage = new ArrayList();
- System.out.println("Foo");
}
-
+
protected MidiInput(MidiInputDevice _device) throws MidiUnavailableException {
this(_device.getDevice());
}
-
+
public String getName() {
javax.sound.midi.MidiDevice.Info info = jDevice.getDeviceInfo();
return info.getName() + " " + info.getVendor();
@@ -60,8 +64,6 @@ public void close() {
plugList.clear();
}
- ArrayList currentMessage;
-
public static void printHex(byte[] b) {
printHex(b, 0, b.length);
}
@@ -90,53 +92,85 @@ public static void printHex(byte[] b, int start, int length) {
}
System.out.println();
}
-
- public void send(final MidiMessage message, final long timeStamp) {
- if ((message.getLength() > 1)) {
- System.out.println("message " + message);
- printHex(message.getMessage());
+ private void addSysexBytes(byte bytes[]) {
+ for (byte b : bytes) {
+ if (b == (byte)0xF7) {
+ currentMessage.add((byte)0xF7);
+ byte msg[] = new byte[currentMessage.size()];
+ for (int i = 0; i < currentMessage.size(); i++) {
+ msg[i] = ((Byte)currentMessage.get(i)).byteValue();
+ }
+ javax.sound.midi.SysexMessage newMsg = new javax.sound.midi.SysexMessage();
+ try {
+ newMsg.setMessage(msg, msg.length);
+ for (Plug plug : plugList)
+ plug.callPlug(this, newMsg);
+ } catch (InvalidMidiDataException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } else {
+ currentMessage.add(b);
+ }
}
+ }
+
+ public void send(final MidiMessage message, final long timeStamp) {
+ // if ((message.getLength() >= 1)) {
+ // System.out.println("message " + message);
+ // printHex(message.getMessage());
+ // }
if (message instanceof javax.sound.midi.SysexMessage || message.getStatus() == (byte)0xF7) {
if (message.getStatus() == 0xF0) {
-// System.out.println("clear message and start new ");
+ // System.out.println("clear message and start new ");
currentMessage.clear(); // no shortcut for sysex messages
currentMessage.add((byte)0xF0);
}
- for (byte b : ((javax.sound.midi.SysexMessage)message).getData()) {
- if (b == (byte)0xF7) {
- currentMessage.add((byte)0xF7);
- byte msg[] = new byte[currentMessage.size()];
- for (int i = 0; i < currentMessage.size(); i++) {
- msg[i] = ((Byte)currentMessage.get(i)).byteValue();
- }
- javax.sound.midi.SysexMessage newMsg = new javax.sound.midi.SysexMessage();
- try {
- newMsg.setMessage(msg, msg.length);
- for (Plug plug : plugList)
- plug.callPlug(this, newMsg);
- } catch (InvalidMidiDataException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- } else {
- currentMessage.add(b);
- }
- }
+ addSysexBytes(((javax.sound.midi.SysexMessage)message).getData());
+ ///
} else {
- if (message.getStatus() >= 0xF8) {
+ //commenting out to enable midi sync messages
+ // if (message.getStatus() >= 0xF8) {
+ // return;
+ // } else {
+ if (message.getStatus() == 0xF7) {
+ addSysexBytes(message.getMessage());
return;
- } else {
-// System.out.println("clear current message " + Integer.toHexString((byte)message.getStatus()));
- currentMessage.clear(); // discard maybe sysex message
}
-// System.out.println("received message ");
-// printHex(message.getMessage());
+ // }
for (Plug plug : plugList)
plug.callPlug(this, message);
+ if (message.getStatus() == SyncEvent.TIMING_CLOCK && divisions != 0){
+ calculatePulseSpace();
+ for (int i = 1; i < divisions; i++){
+ if (pulseTime > 5){
+ try {
+ Thread.sleep(pulseTime);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ for (Plug plug : plugList)
+ plug.callPlug(this, message);
+ }
+ }
+ if (message.getStatus() == SyncEvent.STOP && divisions != 0){
+ pulseOne = 0;
+ pulseTwo = 0;
+ pulseTime = 0;
+ }
}
}
+ private void calculatePulseSpace(){
+ pulseOne = System.nanoTime();
+ if(pulseTwo != 0){
+ pulseTime = TimeUnit.MILLISECONDS.convert((pulseOne - pulseTwo) / divisions, TimeUnit.NANOSECONDS);
+ }
+ pulseTwo = pulseOne;
+ }
/**
* Register a callback method on a specific channel for a specific MIDI command. The value field is the MIDI status byte,
* for example 0x90 for NOTE ON.
@@ -146,15 +180,15 @@ public void send(final MidiMessage message, final long timeStamp) {
* @param value MIDI status byte, -1 for all messages
*/
public void plug(final Object _object,
- final String _methodName,
- final int channel,
- final int value) {
+ final String _methodName,
+ final int channel,
+ final int value) {
if (Plug.objectHasMethod(_object, _methodName)) {
Plug plug = new Plug(_object, _methodName, channel, value);
plugList.add(plug);
}
}
-
+
/**
* Register a callback method for all MIDI messages received on this input.
* @param object Callback object
@@ -165,7 +199,7 @@ public void plug(
final String methodName) {
plug(object, methodName, -1, -1);
}
-
+
/**
* Register a callback method for all MIDI messages received on a specific channel on this input.
* @param object Callback object
@@ -188,7 +222,7 @@ public void plug(
public void plug(Object obj) {
plug(obj, -1);
}
-
+
/**
* Register an object with standard midi callbacks on a specific channels. The callbacks are noteOnReceived(Note),
* noteOffReceived(Note), controllerChangeReceived(Controller), programChangeReceived(ProgramChange) and
View
8 src/rwmidi/MidiInputDevice.java
@@ -48,5 +48,11 @@ public MidiInput createInput(Object obj, int channel) {
MidiInput input = createInput();
input.plug(obj, channel);
return input;
- }
+ }
+
+ public MidiInput createInput(int convertTo) {
+ MidiInput input = createInput();
+ input.divisions = convertTo/24;
+ return input;
+ }
}
View
44 src/rwmidi/MidiOutput.java 100644 → 100755
@@ -13,13 +13,20 @@
public class MidiOutput {
Receiver receiver;
javax.sound.midi.MidiDevice device;
+ static final int MAXPITCHBEND = 16383;
+ static final int MINPITCHBEND = 0;
MidiOutput(javax.sound.midi.MidiDevice device) throws MidiUnavailableException {
this.device = device;
- device.open();
+ if (!device.isOpen())
+ device.open();
receiver = device.getReceiver();
}
+
+ MidiOutput(Receiver _receiver) {
+ receiver = _receiver;
+ }
MidiOutput(MidiOutputDevice _device) throws MidiUnavailableException {
this(_device.getDevice());
@@ -29,6 +36,7 @@ public String getName() {
javax.sound.midi.MidiDevice.Info info = device.getDeviceInfo();
return info.getName() + " " + info.getVendor();
}
+
/**
* Send a NOTE ON message on this output.
@@ -37,7 +45,7 @@ public String getName() {
* @param velocity Note velocity
* @return 1 on success, 0 on error
*/
- public int sendNoteOn(int channel, int note, int velocity) {
+ public synchronized int sendNoteOn(int channel, int note, int velocity) {
ShortMessage msg = new ShortMessage();
try {
msg.setMessage(MidiEvent.NOTE_ON, channel, note, velocity);
@@ -57,7 +65,7 @@ public int sendNoteOn(int channel, int note, int velocity) {
* @param velocity Note velocity
* @return 1 on success, 0 on error
*/
- public int sendNoteOff(int channel, int note, int velocity) {
+ public synchronized int sendNoteOff(int channel, int note, int velocity) {
ShortMessage msg = new ShortMessage();
try {
msg.setMessage(MidiEvent.NOTE_OFF, channel, note, velocity);
@@ -77,7 +85,7 @@ public int sendNoteOff(int channel, int note, int velocity) {
* @param value Controller Change value
* @return 1 on success, 0 on error
*/
- public int sendController(int channel, int cc, int value) {
+ public synchronized int sendController(int channel, int cc, int value) {
ShortMessage msg = new ShortMessage();
try {
msg.setMessage(MidiEvent.CONTROL_CHANGE, channel, cc, value);
@@ -91,12 +99,36 @@ public int sendController(int channel, int cc, int value) {
}
/**
+ * Send a Pitch Bend change message on this output.
+ * @param channel Channel on which to send the message
+ * @param value Pitch Bend value
+ * @return 1 on success, 0 on error
+ */
+ public int sendPitchBend(int channel, int value) {
+ if (value > MAXPITCHBEND) value = MAXPITCHBEND;
+ if (value < MINPITCHBEND) value = MINPITCHBEND;
+ byte lsb = (byte) (value % 128);
+ byte msb = (byte) (value/128);
+
+ ShortMessage msg = new ShortMessage();
+ try {
+ msg.setMessage(MidiEvent.PITCH_BEND, channel, lsb, msb);
+ receiver.send(msg, -1);
+ return 1;
+ } catch (InvalidMidiDataException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ /**
* Send a Program Change on this output
* @param channel Channel on which to send the message
* @param value Program Change value
* @return 1 on success, 0 on error
*/
- public int sendProgramChange(int value) {
+ public synchronized int sendProgramChange(int value) {
ShortMessage msg = new ShortMessage();
try {
msg.setMessage(MidiEvent.PROGRAM_CHANGE, value, -1);
@@ -114,7 +146,7 @@ public int sendProgramChange(int value) {
* @param msg Bytes of the sysex message, have to contain 0xF0 at the beginning and 0xF7 at the end
* @return 1 on success, 0 on error
*/
- public int sendSysex(byte [] msg) {
+ public synchronized int sendSysex(byte [] msg) {
javax.sound.midi.SysexMessage msg2 = new javax.sound.midi.SysexMessage();
try {
msg2.setMessage(msg, msg.length);
View
11 src/rwmidi/SyncEvent.java
@@ -0,0 +1,11 @@
+package rwmidi;
+
+import javax.sound.midi.MidiMessage;
+
+public class SyncEvent extends MidiEvent {
+
+ public SyncEvent(MidiMessage message) {
+ super(message);
+ // TODO Auto-generated constructor stub
+ }
+}
Something went wrong with that request. Please try again.