diff options
author | Mike Lockwood <lockwood@google.com> | 2014-12-09 02:37:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-12-09 02:37:40 +0000 |
commit | ae13230b8799ac3c0902fef1057d7466f2161c44 (patch) | |
tree | cc5526f0b3fc4e39025cd7ec0eb6e3eee938133b /core | |
parent | 7822e6437ac8013dec6c67705e8558ad1f35d69c (diff) | |
parent | b6b9a91c02b7a44cf56943e5358cee68fa4aece5 (diff) | |
download | frameworks_base-ae13230b8799ac3c0902fef1057d7466f2161c44.zip frameworks_base-ae13230b8799ac3c0902fef1057d7466f2161c44.tar.gz frameworks_base-ae13230b8799ac3c0902fef1057d7466f2161c44.tar.bz2 |
Merge "MIDI: API changes to support multiple ports per device"
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/midi/IMidiManager.aidl | 3 | ||||
-rw-r--r-- | core/java/android/midi/MidiDevice.java | 173 | ||||
-rw-r--r-- | core/java/android/midi/MidiDeviceInfo.java | 40 | ||||
-rw-r--r-- | core/java/android/midi/MidiInputPort.java | 53 | ||||
-rw-r--r-- | core/java/android/midi/MidiManager.java | 6 | ||||
-rw-r--r-- | core/java/android/midi/MidiOutputPort.java | 42 | ||||
-rw-r--r-- | core/java/android/midi/MidiPort.java | 43 |
7 files changed, 285 insertions, 75 deletions
diff --git a/core/java/android/midi/IMidiManager.aidl b/core/java/android/midi/IMidiManager.aidl index 06b675b..7a9f887 100644 --- a/core/java/android/midi/IMidiManager.aidl +++ b/core/java/android/midi/IMidiManager.aidl @@ -37,7 +37,8 @@ interface IMidiManager ParcelFileDescriptor openDevice(IBinder token, in MidiDeviceInfo device); // for implementing virtual MIDI devices - MidiDevice registerVirtualDevice(IBinder token, in Bundle properties); + MidiDevice registerVirtualDevice(IBinder token, int numInputPorts, int numOutputPorts, + in Bundle properties); void unregisterVirtualDevice(IBinder token, in MidiDeviceInfo device); // for use by UsbAudioManager diff --git a/core/java/android/midi/MidiDevice.java b/core/java/android/midi/MidiDevice.java index 8954d2b..e704ea0 100644 --- a/core/java/android/midi/MidiDevice.java +++ b/core/java/android/midi/MidiDevice.java @@ -28,7 +28,7 @@ import java.io.IOException; import java.util.ArrayList; /** - * This class is used for sending and receiving data to and from an midi device + * This class is used for sending and receiving data to and from an MIDI device * Instances of this class are created by {@link MidiManager#openDevice}. * This class can also be used to provide the implementation for a virtual device. * @@ -44,21 +44,29 @@ public final class MidiDevice implements Parcelable { private ParcelFileDescriptor mParcelFileDescriptor; private FileInputStream mInputStream; private FileOutputStream mOutputStream; - private final ArrayList<MidiReceiver> mReceivers = new ArrayList<MidiReceiver>(); + + // lazily populated lists of ports + private final MidiInputPort[] mInputPorts; + private final MidiOutputPort[] mOutputPorts; + + // array of receiver lists, indexed by port number + private final ArrayList<MidiReceiver>[] mReceivers; + + private int mReceiverCount; // total number of receivers for all ports /** * Minimum size of packed message as sent through our ParcelFileDescriptor - * 8 bytes for timestamp and 1 to 3 bytes for message + * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message * @hide */ - public static final int MIN_PACKED_MESSAGE_SIZE = 9; + public static final int MIN_PACKED_MESSAGE_SIZE = 10; /** * Maximum size of packed message as sent through our ParcelFileDescriptor - * 8 bytes for timestamp and 1 to 3 bytes for message + * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message * @hide */ - public static final int MAX_PACKED_MESSAGE_SIZE = 11; + public static final int MAX_PACKED_MESSAGE_SIZE = 12; // This thread reads MIDI events from a socket and distributes them to the list of // MidiReceivers attached to this device. @@ -80,27 +88,35 @@ public final class MidiDevice implements Parcelable { int offset = getMessageOffset(buffer, count); int size = getMessageSize(buffer, count); long timestamp = getMessageTimeStamp(buffer, count); + int port = getMessagePortNumber(buffer, count); synchronized (mReceivers) { - for (int i = 0; i < mReceivers.size(); i++) { - MidiReceiver receiver = mReceivers.get(i); - try { - mReceivers.get(i).onPost(buffer, offset, size, timestamp); - } catch (IOException e) { - Log.e(TAG, "post failed"); - deadReceivers.add(receiver); + ArrayList<MidiReceiver> receivers = mReceivers[port]; + if (receivers != null) { + for (int i = 0; i < receivers.size(); i++) { + MidiReceiver receiver = receivers.get(i); + try { + receivers.get(i).onPost(buffer, offset, size, timestamp); + } catch (IOException e) { + Log.e(TAG, "post failed"); + deadReceivers.add(receiver); + } } - } - // remove any receivers that failed - if (deadReceivers.size() > 0) { - for (MidiReceiver receiver: deadReceivers) { - mReceivers.remove(receiver); + // remove any receivers that failed + if (deadReceivers.size() > 0) { + for (MidiReceiver receiver: deadReceivers) { + receivers.remove(receiver); + mReceiverCount--; + } + deadReceivers.clear(); + } + if (receivers.size() == 0) { + mReceivers[port] = null; + } + // exit if we have no receivers left + if (mReceiverCount == 0) { + break; } - deadReceivers.clear(); - } - // exit if we have no receivers left - if (mReceivers.size() == 0) { - break; } } } @@ -110,48 +126,66 @@ public final class MidiDevice implements Parcelable { } }; - // This is the receiver that clients use for sending events to this device. - private final MidiReceiver mReceiver = new MidiReceiver() { - private final byte[] mBuffer = new byte[MAX_PACKED_MESSAGE_SIZE]; - public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException { - synchronized (mBuffer) { - int length = packMessage(msg, offset, count, timestamp, mBuffer); - mOutputStream.write(mBuffer, 0, length); + /** + * MidiDevice should only be instantiated by MidiManager or MidiService + * @hide + */ + public MidiDevice(MidiDeviceInfo deviceInfo, ParcelFileDescriptor pfd) { + mDeviceInfo = deviceInfo; + mParcelFileDescriptor = pfd; + int inputPorts = deviceInfo.getInputPortCount(); + int outputPorts = deviceInfo.getOutputPortCount(); + mInputPorts = new MidiInputPort[inputPorts]; + mOutputPorts = new MidiOutputPort[outputPorts]; + mReceivers = new ArrayList[outputPorts]; + } + + public MidiInputPort openInputPort(int portNumber) { + if (portNumber < 0 || portNumber >= mDeviceInfo.getInputPortCount()) { + throw new IllegalArgumentException("input port number out of range"); + } + synchronized (mInputPorts) { + if (mInputPorts[portNumber] == null) { + mInputPorts[portNumber] = new MidiInputPort(mOutputStream, portNumber); } + return mInputPorts[portNumber]; } - }; + } - // Our MidiSender object, to which clients can attach MidiReceivers. - private final MidiSender mSender = new MidiSender() { - public void connect(MidiReceiver receiver) { - synchronized (mReceivers) { - if (mReceivers.size() == 0) { - mThread.start(); - } - mReceivers.add(receiver); + public MidiOutputPort openOutputPort(int portNumber) { + if (portNumber < 0 || portNumber >= mDeviceInfo.getOutputPortCount()) { + throw new IllegalArgumentException("output port number out of range"); + } + synchronized (mOutputPorts) { + if (mOutputPorts[portNumber] == null) { + mOutputPorts[portNumber] = new MidiOutputPort(this, portNumber); } + return mOutputPorts[portNumber]; } + } - public void disconnect(MidiReceiver receiver) { - synchronized (mReceivers) { - mReceivers.remove(receiver); - if (mReceivers.size() == 0) { - // ??? - } + /* package */ void connect(MidiReceiver receiver, int portNumber) { + synchronized (mReceivers) { + if (mReceivers[portNumber] == null) { + mReceivers[portNumber] = new ArrayList<MidiReceiver>(); + } + mReceivers[portNumber].add(receiver); + if (mReceiverCount++ == 0) { + mThread.start(); } } - }; + } - /** - * MidiDevice should only be instantiated by MidiManager or MidiService - * @hide - */ - public MidiDevice(MidiDeviceInfo deviceInfo, ParcelFileDescriptor pfd) { - mDeviceInfo = deviceInfo; - mParcelFileDescriptor = pfd; + /* package */ void disconnect(MidiReceiver receiver, int portNumber) { + synchronized (mReceivers) { + ArrayList<MidiReceiver> receivers = mReceivers[portNumber]; + if (receivers != null && receivers.remove(receiver)) { + mReceiverCount--; + } + } } - public boolean open() { + /* package */ boolean open() { FileDescriptor fd = mParcelFileDescriptor.getFileDescriptor(); try { mInputStream = new FileInputStream(fd); @@ -187,16 +221,6 @@ public final class MidiDevice implements Parcelable { return mDeviceInfo; } - // returns our MidiReceiver, which clients can use for sending events to this device. - public MidiReceiver getReceiver() { - return mReceiver; - } - - // Returns our MidiSender object, to which clients can attach MidiReceivers. - public MidiSender getSender() { - return mSender; - } - @Override public String toString() { return ("MidiDevice: " + mDeviceInfo.toString() + " fd: " + mParcelFileDescriptor); @@ -236,7 +260,7 @@ public final class MidiDevice implements Parcelable { * @hide */ public static int packMessage(byte[] message, int offset, int size, long timestamp, - byte[] dest) { + int portNumber, byte[] dest) { // pack variable length message first System.arraycopy(message, offset, dest, 0, size); int destOffset = size; @@ -245,6 +269,9 @@ public final class MidiDevice implements Parcelable { dest[destOffset++] = (byte)timestamp; timestamp >>= 8; } + // portNumber is last + dest[destOffset++] = (byte)portNumber; + return destOffset; } @@ -266,8 +293,8 @@ public final class MidiDevice implements Parcelable { * @hide */ public static int getMessageSize(byte[] buffer, int bufferLength) { - // message length is total buffer length minus size of the timestamp - return bufferLength - 8; + // message length is total buffer length minus size of the timestamp and port number + return bufferLength - 9 /* (sizeof(timestamp) + sizeof(portNumber)) */; } /** @@ -289,4 +316,16 @@ public final class MidiDevice implements Parcelable { } return timestamp; } + + /** + * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor + * unpacks port number from packed buffer + * + * @hide + */ + public static int getMessagePortNumber(byte[] buffer, int bufferLength) { + // timestamp follows variable length message data and timestamp + int dataLength = getMessageSize(buffer, bufferLength); + return buffer[dataLength + 8 /* sizeof(timestamp) */]; + } } diff --git a/core/java/android/midi/MidiDeviceInfo.java b/core/java/android/midi/MidiDeviceInfo.java index def6878c..239481b 100644 --- a/core/java/android/midi/MidiDeviceInfo.java +++ b/core/java/android/midi/MidiDeviceInfo.java @@ -39,6 +39,8 @@ public class MidiDeviceInfo implements Parcelable { private final int mType; // USB or virtual private final int mId; // unique ID generated by MidiService + private final int mInputPortCount; + private final int mOutputPortCount; private final Bundle mProperties; // used for USB devices only @@ -77,9 +79,12 @@ public class MidiDeviceInfo implements Parcelable { * MidiDeviceInfo should only be instantiated by MidiService implementation * @hide */ - public MidiDeviceInfo(int type, int id, Bundle properties) { + public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts, + Bundle properties) { mType = type; mId = id; + mInputPortCount = numInputPorts; + mOutputPortCount = numOutputPorts; mProperties = properties; mAlsaCard = -1; mAlsaDevice = -1; @@ -89,10 +94,12 @@ public class MidiDeviceInfo implements Parcelable { * MidiDeviceInfo should only be instantiated by MidiService implementation * @hide */ - public MidiDeviceInfo(int type, int id, Bundle properties, - int alsaCard, int alsaDevice) { + public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts, + Bundle properties, int alsaCard, int alsaDevice) { mType = type; mId = id; + mInputPortCount = numInputPorts; + mOutputPortCount = numOutputPorts; mProperties = properties; mAlsaCard = alsaCard; mAlsaDevice = alsaDevice; @@ -118,6 +125,24 @@ public class MidiDeviceInfo implements Parcelable { } /** + * Returns the device's number of input ports. + * + * @return the number of input ports + */ + public int getInputPortCount() { + return mInputPortCount; + } + + /** + * Returns the device's number of output ports. + * + * @return the number of output ports + */ + public int getOutputPortCount() { + return mOutputPortCount; + } + + /** * Returns the {@link android.os.Bundle} containing the device's properties. * * @return the device's properties @@ -157,7 +182,8 @@ public class MidiDeviceInfo implements Parcelable { @Override public String toString() { return ("MidiDeviceInfo[mType=" + mType + - ",mId=" + mId + + ",mInputPortCount=" + mInputPortCount + + ",mOutputPortCount=" + mOutputPortCount + ",mProperties=" + mProperties + ",mAlsaCard=" + mAlsaCard + ",mAlsaDevice=" + mAlsaDevice); @@ -168,10 +194,12 @@ public class MidiDeviceInfo implements Parcelable { public MidiDeviceInfo createFromParcel(Parcel in) { int type = in.readInt(); int id = in.readInt(); + int inputPorts = in.readInt(); + int outputPorts = in.readInt(); Bundle properties = in.readBundle(); int card = in.readInt(); int device = in.readInt(); - return new MidiDeviceInfo(type, id, properties, card, device); + return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties, card, device); } public MidiDeviceInfo[] newArray(int size) { @@ -186,6 +214,8 @@ public class MidiDeviceInfo implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mType); parcel.writeInt(mId); + parcel.writeInt(mInputPortCount); + parcel.writeInt(mOutputPortCount); parcel.writeBundle(mProperties); parcel.writeInt(mAlsaCard); parcel.writeInt(mAlsaDevice); diff --git a/core/java/android/midi/MidiInputPort.java b/core/java/android/midi/MidiInputPort.java new file mode 100644 index 0000000..583c367 --- /dev/null +++ b/core/java/android/midi/MidiInputPort.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.midi; + +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * This class is used for sending data to a port on a MIDI device + * + * @hide + */ +public final class MidiInputPort extends MidiPort implements MidiReceiver { + + private final FileOutputStream mOutputStream; + // buffer to use for sending messages out our output stream + private final byte[] mBuffer = new byte[MidiDevice.MAX_PACKED_MESSAGE_SIZE]; + + /* package */ MidiInputPort(FileOutputStream outputStream, int portNumber) { + super(portNumber); + mOutputStream = outputStream; + } + + /** + * Writes a MIDI message to the input port + * + * @param msg message bytes + * @param offset offset of first byte of the message in msg array + * @param count size of the message in bytes + * @param timestamp future time to post the message + */ + public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException { + synchronized (mBuffer) { + int length = MidiDevice.packMessage(msg, offset, count, timestamp, mPortNumber, + mBuffer); + mOutputStream.write(mBuffer, 0, length); + } + } +} diff --git a/core/java/android/midi/MidiManager.java b/core/java/android/midi/MidiManager.java index ec869b7..f4d1918 100644 --- a/core/java/android/midi/MidiManager.java +++ b/core/java/android/midi/MidiManager.java @@ -132,9 +132,11 @@ public class MidiManager { // Use this if you want to register and implement a virtual device. // The MidiDevice returned by this method is the proxy you use to implement the device. - public MidiDevice createVirtualDevice(Bundle properties) { + public MidiDevice createVirtualDevice(int numInputPorts, int numOutputPorts, + Bundle properties) { try { - MidiDevice device = mService.registerVirtualDevice(mToken, properties); + MidiDevice device = mService.registerVirtualDevice(mToken, + numInputPorts, numOutputPorts, properties); if (device != null && !device.open()) { device = null; } diff --git a/core/java/android/midi/MidiOutputPort.java b/core/java/android/midi/MidiOutputPort.java new file mode 100644 index 0000000..69a33cb --- /dev/null +++ b/core/java/android/midi/MidiOutputPort.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.midi; + +import java.io.FileInputStream; + +/** + * This class is used for receiving data to a port on a MIDI device + * + * @hide + */ +public final class MidiOutputPort extends MidiPort implements MidiSender { + + private final MidiDevice mDevice; + + /* package */ MidiOutputPort(MidiDevice device, int portNumber) { + super(portNumber); + mDevice = device; + } + + public void connect(MidiReceiver receiver) { + mDevice.connect(receiver, mPortNumber); + } + + public void disconnect(MidiReceiver receiver) { + mDevice.disconnect(receiver, mPortNumber); + } +} diff --git a/core/java/android/midi/MidiPort.java b/core/java/android/midi/MidiPort.java new file mode 100644 index 0000000..e94f62d --- /dev/null +++ b/core/java/android/midi/MidiPort.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.midi; + +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * This class represents a MIDI input or output port + * + * @hide + */ +public class MidiPort { + + protected final int mPortNumber; + + /* package */ MidiPort(int portNumber) { + mPortNumber = portNumber; + } + + /** + * Returns the port number of this port + * + * @return the port's port number + */ + public int getPortNumber() { + return mPortNumber; + } +} |