1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-19 00:51:12 +02:00

Update portmidi to SVN r227.

This commit is contained in:
lllucius
2013-10-31 07:33:41 +00:00
parent a30f9e913b
commit bb63fa0d07
118 changed files with 15017 additions and 12291 deletions

View File

@@ -0,0 +1,539 @@
package jportmidi;
/* PortMidi is a general class intended for any Java program using
the PortMidi library. It encapsulates JPortMidiApi with a more
object-oriented interface. A single PortMidi object can manage
up to one input stream and one output stream.
This class is not safely callable from multiple threads. It
is the client's responsibility to periodically call the Poll
method which checks for midi input and handles it.
*/
import jportmidi.*;
import jportmidi.JPortMidiApi.*;
public class JPortMidi {
// timecode to send message immediately
public final int NOW = 0;
// midi codes
public final int MIDI_NOTE_OFF = 0x80;
public final int MIDI_NOTE_ON = 0x90;
public final int CTRL_ALL_OFF = 123;
public final int MIDI_PITCH_BEND = 0xE0;
public final int MIDI_CLOCK = 0xF8;
public final int MIDI_CONTROL = 0xB0;
public final int MIDI_PROGRAM = 0xC0;
public final int MIDI_START = 0xFA;
public final int MIDI_STOP = 0xFC;
public final int MIDI_POLY_TOUCH = 0xA0;
public final int MIDI_TOUCH = 0xD0;
// error code -- cannot refresh device list while stream is open:
public final int pmStreamOpen = -5000;
public final int pmOutputNotOpen = -4999;
// access to JPortMidiApi is through a single, global instance
private static JPortMidiApi pm;
// a reference count tracks how many objects have it open
private static int pmRefCount = 0;
private static int openCount = 0;
public int error; // user can check here for error codes
private PortMidiStream input;
private PortMidiStream output;
private PmEvent buffer;
protected int timestamp; // remember timestamp from incoming messages
protected boolean trace = false; // used to print midi msgs for debugging
public JPortMidi() throws JPortMidiException {
if (pmRefCount == 0) {
pm = new JPortMidiApi();
pmRefCount++;
System.out.println("Calling Pm_Initialize");
checkError(pm.Pm_Initialize());
System.out.println("Called Pm_Initialize");
}
buffer = new PmEvent();
}
public boolean getTrace() { return trace; }
// set the trace flag and return previous value
public boolean setTrace(boolean flag) {
boolean previous = trace;
trace = flag;
return previous;
}
// WARNING: you must not call this if any devices are open
public void refreshDeviceLists()
throws JPortMidiException
{
if (openCount > 0) {
throw new JPortMidiException(pmStreamOpen,
"RefreshDeviceLists called while stream is open");
}
checkError(pm.Pm_Terminate());
checkError(pm.Pm_Initialize());
}
// there is no control over when/whether this is called, but it seems
// to be a good idea to close things when this object is collected
public void finalize() {
if (input != null) {
error = pm.Pm_Close(input);
}
if (input != null) {
int rslt = pm.Pm_Close(output);
// we may lose an error code from closing output, but don't
// lose any real error from closing input...
if (error == pm.pmNoError) error = rslt;
}
pmRefCount--;
if (pmRefCount == 0) {
error = pm.Pm_Terminate();
}
}
int checkError(int err) throws JPortMidiException
{
// note that Pm_Read and Pm_Write return positive result values
// which are not errors, so compare with >=
if (err >= pm.pmNoError) return err;
if (err == pm.pmHostError) {
throw new JPortMidiException(err, pm.Pm_GetHostErrorText());
} else {
throw new JPortMidiException(err, pm.Pm_GetErrorText(err));
}
}
// ******** ACCESS TO TIME ***********
public void timeStart(int resolution) throws JPortMidiException {
checkError(pm.Pt_TimeStart(resolution));
}
public void timeStop() throws JPortMidiException {
checkError(pm.Pt_TimeStop());
}
public int timeGet() {
return pm.Pt_Time();
}
public boolean timeStarted() {
return pm.Pt_TimeStarted();
}
// ******* QUERY DEVICE INFORMATION *********
public int countDevices() throws JPortMidiException {
return checkError(pm.Pm_CountDevices());
}
public int getDefaultInputDeviceID() throws JPortMidiException {
return checkError(pm.Pm_GetDefaultInputDeviceID());
}
public int getDefaultOutputDeviceID() throws JPortMidiException {
return checkError(pm.Pm_GetDefaultOutputDeviceID());
}
public String getDeviceInterf(int i) {
return pm.Pm_GetDeviceInterf(i);
}
public String getDeviceName(int i) {
return pm.Pm_GetDeviceName(i);
}
public boolean getDeviceInput(int i) {
return pm.Pm_GetDeviceInput(i);
}
public boolean getDeviceOutput(int i) {
return pm.Pm_GetDeviceOutput(i);
}
// ********** MIDI INTERFACE ************
public boolean isOpenInput() {
return input != null;
}
public void openInput(int inputDevice, int bufferSize)
throws JPortMidiException
{
openInput(inputDevice, "", bufferSize);
}
public void openInput(int inputDevice, String inputDriverInfo, int bufferSize)
throws JPortMidiException
{
if (isOpenInput()) pm.Pm_Close(input);
else input = new PortMidiStream();
if (trace) {
System.out.println("openInput " + getDeviceName(inputDevice));
}
checkError(pm.Pm_OpenInput(input, inputDevice,
inputDriverInfo, bufferSize));
// if no exception, then increase count of open streams
openCount++;
}
public boolean isOpenOutput() {
return output != null;
}
public void openOutput(int outputDevice, int bufferSize, int latency)
throws JPortMidiException
{
openOutput(outputDevice, "", bufferSize, latency);
}
public void openOutput(int outputDevice, String outputDriverInfo,
int bufferSize, int latency) throws JPortMidiException {
if (isOpenOutput()) pm.Pm_Close(output);
else output = new PortMidiStream();
if (trace) {
System.out.println("openOutput " + getDeviceName(outputDevice));
}
checkError(pm.Pm_OpenOutput(output, outputDevice, outputDriverInfo,
bufferSize, latency));
// if no exception, then increase count of open streams
openCount++;
}
public void setFilter(int filters) throws JPortMidiException {
if (input == null) return; // no effect if input not open
checkError(pm.Pm_SetFilter(input, filters));
}
public void setChannelMask(int mask) throws JPortMidiException {
if (input == null) return; // no effect if input not open
checkError(pm.Pm_SetChannelMask(input, mask));
}
public void abort() throws JPortMidiException {
if (output == null) return; // no effect if output not open
checkError(pm.Pm_Abort(output));
}
// In keeping with the idea that this class represents an input and output,
// there are separate Close methods for input and output streams, avoiding
// the need for clients to ever deal directly with a stream object
public void closeInput() throws JPortMidiException {
if (input == null) return; // no effect if input not open
checkError(pm.Pm_Close(input));
input = null;
openCount--;
}
public void closeOutput() throws JPortMidiException {
if (output == null) return; // no effect if output not open
checkError(pm.Pm_Close(output));
output = null;
openCount--;
}
// Poll should be called by client to process input messages (if any)
public void poll() throws JPortMidiException {
if (input == null) return; // does nothing until input is opened
while (true) {
int rslt = pm.Pm_Read(input, buffer);
checkError(rslt);
if (rslt == 0) return; // no more messages
handleMidiIn(buffer);
}
}
public void writeShort(int when, int msg) throws JPortMidiException {
if (output == null)
throw new JPortMidiException(pmOutputNotOpen,
"Output stream not open");
if (trace) {
System.out.println("writeShort: " + Integer.toHexString(msg));
}
checkError(pm.Pm_WriteShort(output, when, msg));
}
public void writeSysEx(int when, byte msg[]) throws JPortMidiException {
if (output == null)
throw new JPortMidiException(pmOutputNotOpen,
"Output stream not open");
if (trace) {
System.out.print("writeSysEx: ");
for (int i = 0; i < msg.length; i++) {
System.out.print(Integer.toHexString(msg[i]));
}
System.out.print("\n");
}
checkError(pm.Pm_WriteSysEx(output, when, msg));
}
public int midiChanMessage(int chan, int status, int data1, int data2) {
return (((data2 << 16) & 0xFF0000) |
((data1 << 8) & 0xFF00) |
(status & 0xF0) |
(chan & 0xF));
}
public int midiMessage(int status, int data1, int data2) {
return ((((data2) << 16) & 0xFF0000) |
(((data1) << 8) & 0xFF00) |
((status) & 0xFF));
}
public void midiAllOff(int channel) throws JPortMidiException {
midiAllOff(channel, NOW);
}
public void midiAllOff(int chan, int when) throws JPortMidiException {
writeShort(when, midiChanMessage(chan, MIDI_CONTROL, CTRL_ALL_OFF, 0));
}
public void midiPitchBend(int chan, int value) throws JPortMidiException {
midiPitchBend(chan, value, NOW);
}
public void midiPitchBend(int chan, int value, int when)
throws JPortMidiException {
writeShort(when,
midiChanMessage(chan, MIDI_PITCH_BEND, value, value >> 7));
}
public void midiClock() throws JPortMidiException {
midiClock(NOW);
}
public void midiClock(int when) throws JPortMidiException {
writeShort(when, midiMessage(MIDI_CLOCK, 0, 0));
}
public void midiControl(int chan, int control, int value)
throws JPortMidiException {
midiControl(chan, control, value, NOW);
}
public void midiControl(int chan, int control, int value, int when)
throws JPortMidiException {
writeShort(when, midiChanMessage(chan, MIDI_CONTROL, control, value));
}
public void midiNote(int chan, int pitch, int vel)
throws JPortMidiException {
midiNote(chan, pitch, vel, NOW);
}
public void midiNote(int chan, int pitch, int vel, int when)
throws JPortMidiException {
writeShort(when, midiChanMessage(chan, MIDI_NOTE_ON, pitch, vel));
}
public void midiProgram(int chan, int program)
throws JPortMidiException {
midiProgram(chan, program, NOW);
}
public void midiProgram(int chan, int program, int when)
throws JPortMidiException {
writeShort(when, midiChanMessage(chan, MIDI_PROGRAM, program, 0));
}
public void midiStart()
throws JPortMidiException {
midiStart(NOW);
}
public void midiStart(int when)
throws JPortMidiException {
writeShort(when, midiMessage(MIDI_START, 0, 0));
}
public void midiStop()
throws JPortMidiException {
midiStop(NOW);
}
public void midiStop(int when)
throws JPortMidiException {
writeShort(when, midiMessage(MIDI_STOP, 0, 0));
}
public void midiPolyTouch(int chan, int key, int value)
throws JPortMidiException {
midiPolyTouch(chan, key, value, NOW);
}
public void midiPolyTouch(int chan, int key, int value, int when)
throws JPortMidiException {
writeShort(when, midiChanMessage(chan, MIDI_POLY_TOUCH, key, value));
}
public void midiTouch(int chan, int value)
throws JPortMidiException {
midiTouch(chan, value, NOW);
}
public void midiTouch(int chan, int value, int when)
throws JPortMidiException {
writeShort(when, midiChanMessage(chan, MIDI_TOUCH, value, 0));
}
// ****** now we implement the message handlers ******
// an array for incoming sysex messages that can grow.
// The downside is that after getting a message, we
private byte sysexBuffer[] = null;
private int sysexBufferIndex = 0;
void sysexBufferReset() {
sysexBufferIndex = 0;
if (sysexBuffer == null) sysexBuffer = new byte[256];
}
void sysexBufferCheck() {
if (sysexBuffer.length < sysexBufferIndex + 4) {
byte bigger[] = new byte[sysexBuffer.length * 2];
for (int i = 0; i < sysexBufferIndex; i++) {
bigger[i] = sysexBuffer[i];
}
sysexBuffer = bigger;
}
// now we have space to write some bytes
}
// call this to insert Sysex and EOX status bytes
// call sysexBufferAppendBytes to insert anything else
void sysexBufferAppendStatus(byte status) {
sysexBuffer[sysexBufferIndex++] = status;
}
void sysexBufferAppendBytes(int msg, int len) {
for (int i = 0; i < len; i++) {
byte b = (byte) msg;
if ((msg & 0x80) != 0) {
if (b == 0xF7) { // end of sysex
sysexBufferAppendStatus(b);
sysex(sysexBuffer, sysexBufferIndex);
return;
}
// recursively handle embedded real-time messages
PmEvent buffer = new PmEvent();
buffer.timestamp = timestamp;
buffer.message = b;
handleMidiIn(buffer);
} else {
sysexBuffer[sysexBufferIndex++] = b;
}
msg = msg >> 8;
}
}
void sysexBegin(int msg) {
sysexBufferReset(); // start from 0, we have at least 256 bytes now
sysexBufferAppendStatus((byte) (msg & 0xFF)); // first byte is special
sysexBufferAppendBytes(msg >> 8, 3); // process remaining bytes
}
public void handleMidiIn(PmEvent buffer)
{
if (trace) {
System.out.println("handleMidiIn: " +
Integer.toHexString(buffer.message));
}
// rather than pass timestamps to every handler, where typically
// timestamps are ignored, just save the timestamp as a member
// variable where methods can access it if they want it
timestamp = buffer.timestamp;
int status = buffer.message & 0xFF;
if (status < 0x80) {
sysexBufferCheck(); // make enough space
sysexBufferAppendBytes(buffer.message, 4); // process 4 bytes
return;
}
int command = status & 0xF0;
int channel = status & 0x0F;
int data1 = (buffer.message >> 8) & 0xFF;
int data2 = (buffer.message >> 16) & 0xFF;
switch (command) {
case MIDI_NOTE_OFF:
noteOff(channel, data1, data2); break;
case MIDI_NOTE_ON:
if (data2 > 0) {
noteOn(channel, data1, data2); break;
} else {
noteOff(channel, data1);
}
break;
case MIDI_CONTROL:
control(channel, data1, data2); break;
case MIDI_POLY_TOUCH:
polyTouch(channel, data1, data2); break;
case MIDI_TOUCH:
touch(channel, data1); break;
case MIDI_PITCH_BEND:
pitchBend(channel, (data1 + (data2 << 7)) - 8192); break;
case MIDI_PROGRAM:
program(channel, data1); break;
case 0xF0:
switch (channel) {
case 0: sysexBegin(buffer.message); break;
case 1: mtcQuarterFrame(data1);
case 2: songPosition(data1 + (data2 << 7)); break;
case 3: songSelect(data1); break;
case 4: /* unused */ break;
case 5: /* unused */ break;
case 6: tuneRequest(); break;
case 7: sysexBufferAppendBytes(buffer.message, buffer.message); break;
case 8: clock(); break;
case 9: tick(); break;
case 0xA: clockStart(); break;
case 0xB: clockContinue(); break;
case 0xC: clockStop(); break;
case 0xD: /* unused */ break;
case 0xE: activeSense(); break;
case 0xF: reset(); break;
}
}
}
// the value ranges from +8181 to -8192. The interpretation is
// synthesizer dependent. Often the range is +/- one whole step
// (two semitones), but the range is usually adjustable within
// the synthesizer.
void pitchBend(int channel, int value) { return; }
void control(int channel, int control, int value) { return; }
void noteOn(int channel, int pitch, int velocity) { return; }
// you can handle velocity in note-off if you want, but the default
// is to drop the velocity and call the simpler NoteOff handler
void noteOff(int channel, int pitch, int velocity) {
noteOff(channel, pitch);
}
// if the subclass wants to implement NoteOff with velocity, it
// should override the following to make sure all NoteOffs are handled
void noteOff(int channel, int pitch) { return; }
void program(int channel, int program) { return; }
// the byte array may be bigger than the message, length tells how
// many bytes in the array are part of the message
void sysex(byte[] msg, int length) { return; }
void polyTouch(int channel, int key, int value) { return; }
void touch(int channel, int value) { return; }
void mtcQuarterFrame(int value) { return; }
// the value is a 14-bit integer representing 16th notes
void songPosition(int value) { return; }
void songSelect(int value) { return; }
void tuneRequest() { return; }
void clock() { return; } // represents 1/24th of a quarter note
void tick() { return; } // represents 10ms
void clockStart() { return; }
void clockStop() { return; }
void clockContinue() { return; }
void activeSense() { return; }
void reset() { return; }
}

View File

@@ -0,0 +1,117 @@
package jportmidi;
public class JPortMidiApi {
public static class PortMidiStream {
private long address;
}
public static class PmEvent {
public int message;
public int timestamp;
}
// PmError bindings
public final int pmNoError = 0;
public final int pmNoData = 0;
public final int pmGotData = -1;
public final int pmHostError = -10000;
public final int pmInvalidDeviceId = -9999;
public final int pmInsufficientMemory = -9998;
public final int pmBufferTooSmall = -9997;
public final int pmBufferOverflow = -9996;
public final int pmBadPtr = -9995;
public final int pmBadData = -9994;
public final int pmInternalError = -9993;
public final int pmBufferMaxSize = -9992;
static public native int Pm_Initialize();
static public native int Pm_Terminate();
static public native int Pm_HasHostError(PortMidiStream stream);
static public native String Pm_GetErrorText(int errnum);
static public native String Pm_GetHostErrorText();
final int pmNoDevice = -1;
static public native int Pm_CountDevices();
static public native int Pm_GetDefaultInputDeviceID();
static public native int Pm_GetDefaultOutputDeviceID();
static public native String Pm_GetDeviceInterf(int i);
static public native String Pm_GetDeviceName(int i);
static public native boolean Pm_GetDeviceInput(int i);
static public native boolean Pm_GetDeviceOutput(int i);
static public native int Pm_OpenInput(PortMidiStream stream,
int inputDevice,
String inputDriverInfo,
int bufferSize);
static public native int Pm_OpenOutput(PortMidiStream stream,
int outputDevice,
String outnputDriverInfo,
int bufferSize,
int latency);
final static public int PM_FILT_ACTIVE = (1 << 0x0E);
final static public int PM_FILT_SYSEX = (1 << 0x00);
final static public int PM_FILT_CLOCK = (1 << 0x08);
final static public int PM_FILT_PLAY =
(1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B);
final static public int PM_FILT_TICK = (1 << 0x09);
final static public int PM_FILT_FD = (1 << 0x0D);
final static public int PM_FILT_UNDEFINED = PM_FILT_FD;
final static public int PM_FILT_RESET = (1 << 0x0F);
final static public int PM_FILT_REALTIME =
PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK;
final static public int PM_FILT_NOTE = (1 << 0x19) | (1 << 0x18);
final static public int PM_FILT_CHANNEL_AFTERTOUCH = (1 << 0x1D);
final static public int PM_FILT_POLY_AFTERTOUCH = (1 << 0x1A);
final static public int PM_FILT_AFTERTOUCH =
(PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH);
final static public int PM_FILT_PROGRAM = (1 << 0x1C);
final static public int PM_FILT_CONTROL = (1 << 0x1B);
final static public int PM_FILT_PITCHBEND = (1 << 0x1E);
final static public int PM_FILT_MTC = (1 << 0x01);
final static public int PM_FILT_SONG_POSITION = (1 << 0x02);
final static public int PM_FILT_SONG_SELECT = (1 << 0x03);
final static public int PM_FILT_TUNE = (1 << 0x06);
final static public int PM_FILT_SYSTEMCOMMON =
(PM_FILT_MTC | PM_FILT_SONG_POSITION |
PM_FILT_SONG_SELECT | PM_FILT_TUNE);
static public native int Pm_SetFilter(PortMidiStream stream, int filters);
static public int Pm_Channel(int channel) { return 1 << channel; }
final static public native int Pm_SetChannelMask(PortMidiStream stream,
int mask);
final static public native int Pm_Abort(PortMidiStream stream);
final static public native int Pm_Close(PortMidiStream stream);
static public int Pm_Message(int status, int data1, int data2) {
return (((data2 << 16) & 0xFF0000) |
((data1 << 8) & 0xFF00) |
(status & 0xFF));
}
static public int Pm_MessageStatus(int msg) {
return msg & 0xFF;
}
static public int Pm_MessageData1(int msg) {
return (msg >> 8) & 0xFF;
}
static public int Pm_MessageData2(int msg) {
return (msg >> 16) & 0xFF;
}
// only supports reading one buffer at a time
static public native int Pm_Read(PortMidiStream stream, PmEvent buffer);
static public native int Pm_Poll(PortMidiStream stream);
// only supports writing one buffer at a time
static public native int Pm_Write(PortMidiStream stream, PmEvent buffer);
static public native int Pm_WriteShort(PortMidiStream stream,
int when, int msg);
static public native int Pm_WriteSysEx(PortMidiStream stream,
int when, byte msg[]);
public final int ptNoError = 0;
public final int ptAlreadyStarted = -10000;
public final int ptAlreadyStopped = -9999;
public final int PtInsufficientMemory = -9998;
static public native int Pt_TimeStart(int resolution);
static public native int Pt_TimeStop();
static public native int Pt_Time();
static public native boolean Pt_TimeStarted();
static {
System.out.println("Loading pmjni");
System.loadLibrary("pmjni");
System.out.println("done loading pmjni");
}
}

View File

@@ -0,0 +1,12 @@
// JPortMidiException -- thrown by JPortMidi methods
package jportmidi;
public class JPortMidiException extends Exception {
public int error = 0;
public JPortMidiException(int err, String msg) {
super(msg);
error = err;
}
}