blob: b550ed4711c19c607b890eddf96c884af6772aae [file] [log] [blame]
/*
* 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 android.os.ParcelFileDescriptor;
import android.util.Log;
import libcore.io.IoUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
/**
* This class is used for receiving data to a port on a MIDI device
*
* @hide
*/
public class MidiOutputPort extends MidiPort implements MidiSender {
private static final String TAG = "MidiOutputPort";
private final FileInputStream mInputStream;
// array of receiver lists, indexed by port number
private final ArrayList<MidiReceiver> mReceivers = new ArrayList<MidiReceiver>();
private int mReceiverCount; // total number of receivers for all ports
// This thread reads MIDI events from a socket and distributes them to the list of
// MidiReceivers attached to this device.
private final Thread mThread = new Thread() {
@Override
public void run() {
byte[] buffer = new byte[MAX_PACKED_MESSAGE_SIZE];
ArrayList<MidiReceiver> deadReceivers = new ArrayList<MidiReceiver>();
try {
while (true) {
// read next event
int count = mInputStream.read(buffer);
if (count < 0) {
break;
} else if (count < MIN_PACKED_MESSAGE_SIZE || count > MAX_PACKED_MESSAGE_SIZE) {
Log.e(TAG, "Number of bytes read out of range: " + count);
continue;
}
int offset = getMessageOffset(buffer, count);
int size = getMessageSize(buffer, count);
long timestamp = getMessageTimeStamp(buffer, count);
synchronized (mReceivers) {
for (int i = 0; i < mReceivers.size(); i++) {
MidiReceiver receiver = mReceivers.get(i);
try {
receiver.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);
mReceiverCount--;
}
deadReceivers.clear();
}
// exit if we have no receivers left
if (mReceiverCount == 0) {
break;
}
}
}
} catch (IOException e) {
Log.e(TAG, "read failed");
// report I/O failure
IoUtils.closeQuietly(mInputStream);
onIOException();
}
}
};
/* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) {
super(portNumber);
mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
}
/**
* Connects a {@link MidiReceiver} to the output port to allow receiving
* MIDI messages from the port.
*
* @param receiver the receiver to connect
*/
public void connect(MidiReceiver receiver) {
synchronized (mReceivers) {
mReceivers.add(receiver);
if (mReceiverCount++ == 0) {
mThread.start();
}
}
}
/**
* Disconnects a {@link MidiReceiver} from the output port.
*
* @param receiver the receiver to connect
*/
public void disconnect(MidiReceiver receiver) {
synchronized (mReceivers) {
if (mReceivers.remove(receiver)) {
mReceiverCount--;
}
}
}
@Override
public void close() throws IOException {
mInputStream.close();
}
}