diff options
author | Yuichi Araki <yaraki@google.com> | 2015-08-06 14:21:27 +0900 |
---|---|---|
committer | Trevor Johns <trevorjohns@google.com> | 2015-08-26 14:17:25 -0700 |
commit | e1a5e9728e2ed10139974a9642c9f1aa9c13c0d1 (patch) | |
tree | b5108aa5d44186b952d4d6f92ecdeef6b75968da /common | |
parent | 57690c21113d5dff8b89b1fb76a5ac94bdbbefab (diff) | |
download | android-e1a5e9728e2ed10139974a9642c9f1aa9c13c0d1.tar.gz |
MidiSynth: Add a new sample
Bug: 21693466
Change-Id: Ib6a7c0802e46e2f5feefa0b31b94dea5cad367d7
(cherry picked from commit b8f8c1d198d7ed3434289625668bad296de4794b)
Diffstat (limited to 'common')
7 files changed, 297 insertions, 84 deletions
diff --git a/common/src/java/com/example/android/common/midi/MidiInputPortSelector.java b/common/src/java/com/example/android/common/midi/MidiInputPortSelector.java index 7ca22722..7c665bac 100644 --- a/common/src/java/com/example/android/common/midi/MidiInputPortSelector.java +++ b/common/src/java/com/example/android/common/midi/MidiInputPortSelector.java @@ -32,7 +32,6 @@ import java.io.IOException; * Manages a Spinner for selecting a MidiInputPort. */ public class MidiInputPortSelector extends MidiPortSelector { - private static final String TAG = "MidiInputPortSelector"; private MidiInputPort mInputPort; private MidiDevice mOpenDevice; @@ -44,27 +43,30 @@ public class MidiInputPortSelector extends MidiPortSelector { */ public MidiInputPortSelector(MidiManager midiManager, Activity activity, int spinnerId) { - super(midiManager, activity, spinnerId, TYPE_INPUT); + super(midiManager, activity, spinnerId, MidiDeviceInfo.PortInfo.TYPE_INPUT); } @Override public void onPortSelected(final MidiPortWrapper wrapper) { - onClose(); - + close(); final MidiDeviceInfo info = wrapper.getDeviceInfo(); if (info != null) { mMidiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() { @Override public void onDeviceOpened(MidiDevice device) { if (device == null) { - Log.e(TAG, "could not open " + info); + Log.e(MidiConstants.TAG, "could not open " + info); } else { mOpenDevice = device; mInputPort = mOpenDevice.openInputPort( wrapper.getPortIndex()); + if (mInputPort == null) { + Log.e(MidiConstants.TAG, "could not open input port on " + info); + } } } - }, new Handler(Looper.getMainLooper())); + }, null); + // Don't run the callback on the UI thread because openInputPort might take a while. } } @@ -76,6 +78,7 @@ public class MidiInputPortSelector extends MidiPortSelector { public void onClose() { try { if (mInputPort != null) { + Log.i(MidiConstants.TAG, "MidiInputPortSelector.onClose() - close port"); mInputPort.close(); } mInputPort = null; @@ -84,7 +87,7 @@ public class MidiInputPortSelector extends MidiPortSelector { } mOpenDevice = null; } catch (IOException e) { - Log.e(TAG, "cleanup failed", e); + Log.e(MidiConstants.TAG, "cleanup failed", e); } } } diff --git a/common/src/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java b/common/src/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java new file mode 100644 index 00000000..ca1ade48 --- /dev/null +++ b/common/src/java/com/example/android/common/midi/MidiOutputPortConnectionSelector.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 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 com.example.android.common.midi; + +import android.app.Activity; +import android.media.midi.MidiDeviceInfo; +import android.media.midi.MidiManager; +import android.util.Log; + +import java.io.IOException; + +/** + * Select an output port and connect it to a destination input port. + */ +public class MidiOutputPortConnectionSelector extends MidiPortSelector { + + private MidiPortConnector mSynthConnector; + private MidiDeviceInfo mDestinationDeviceInfo; + private int mDestinationPortIndex; + private MidiPortConnector.OnPortsConnectedListener mConnectedListener; + + /** + * @param midiManager + * @param activity + * @param spinnerId + * @param type + */ + public MidiOutputPortConnectionSelector(MidiManager midiManager, + Activity activity, int spinnerId, + MidiDeviceInfo destinationDeviceInfo, int destinationPortIndex) { + super(midiManager, activity, spinnerId, + MidiDeviceInfo.PortInfo.TYPE_OUTPUT); + mDestinationDeviceInfo = destinationDeviceInfo; + mDestinationPortIndex = destinationPortIndex; + } + + @Override + public void onPortSelected(final MidiPortWrapper wrapper) { + Log.i(MidiConstants.TAG, "connectPortToSynth: " + wrapper); + onClose(); + if (wrapper.getDeviceInfo() != null) { + mSynthConnector = new MidiPortConnector(mMidiManager); + mSynthConnector.connectToDevicePort(wrapper.getDeviceInfo(), + wrapper.getPortIndex(), mDestinationDeviceInfo, + mDestinationPortIndex, + // not safe on UI thread + mConnectedListener, null); + } + } + + @Override + public void onClose() { + try { + if (mSynthConnector != null) { + mSynthConnector.close(); + mSynthConnector = null; + } + } catch (IOException e) { + Log.e(MidiConstants.TAG, "Exception in closeSynthResources()", e); + } + } + + /** + * @param myPortsConnectedListener + */ + public void setConnectedListener( + MidiPortConnector.OnPortsConnectedListener connectedListener) { + mConnectedListener = connectedListener; + } +} diff --git a/common/src/java/com/example/android/common/midi/MidiOutputPortSelector.java b/common/src/java/com/example/android/common/midi/MidiOutputPortSelector.java index d01d3041..5aebf727 100644 --- a/common/src/java/com/example/android/common/midi/MidiOutputPortSelector.java +++ b/common/src/java/com/example/android/common/midi/MidiOutputPortSelector.java @@ -32,10 +32,7 @@ import java.io.IOException; * Manages a Spinner for selecting a MidiOutputPort. */ public class MidiOutputPortSelector extends MidiPortSelector { - - private static final String TAG = "MidiOutputPortSelector"; - - private MidiOutputPort mSender; + private MidiOutputPort mOutputPort; private MidiDispatcher mDispatcher = new MidiDispatcher(); private MidiDevice mOpenDevice; @@ -46,13 +43,13 @@ public class MidiOutputPortSelector extends MidiPortSelector { */ public MidiOutputPortSelector(MidiManager midiManager, Activity activity, int spinnerId) { - super(midiManager, activity, spinnerId, TYPE_OUTPUT); + super(midiManager, activity, spinnerId, MidiDeviceInfo.PortInfo.TYPE_OUTPUT); } @Override public void onPortSelected(final MidiPortWrapper wrapper) { - Log.i(TAG, "onPortSelected: " + wrapper); - onClose(); + Log.i(MidiConstants.TAG, "onPortSelected: " + wrapper); + close(); final MidiDeviceInfo info = wrapper.getDeviceInfo(); if (info != null) { @@ -61,35 +58,36 @@ public class MidiOutputPortSelector extends MidiPortSelector { @Override public void onDeviceOpened(MidiDevice device) { if (device == null) { - Log.e(TAG, "could not open " + info); + Log.e(MidiConstants.TAG, "could not open " + info); } else { mOpenDevice = device; - mSender = device.openOutputPort(wrapper.getPortIndex()); - if (mSender == null) { - Log.e(TAG, - "could not get sender for " + info); + mOutputPort = device.openOutputPort(wrapper.getPortIndex()); + if (mOutputPort == null) { + Log.e(MidiConstants.TAG, + "could not open output port for " + info); return; } - mSender.connect(mDispatcher); + mOutputPort.connect(mDispatcher); } } - }, new Handler(Looper.getMainLooper())); + }, null); + // Don't run the callback on the UI thread because openOutputPort might take a while. } } @Override public void onClose() { try { - if (mSender != null) { - mSender.disconnect(mDispatcher); + if (mOutputPort != null) { + mOutputPort.disconnect(mDispatcher); } - mSender = null; + mOutputPort = null; if (mOpenDevice != null) { mOpenDevice.close(); } mOpenDevice = null; } catch (IOException e) { - Log.e(TAG, "cleanup failed", e); + Log.e(MidiConstants.TAG, "cleanup failed", e); } } diff --git a/common/src/java/com/example/android/common/midi/MidiPortConnector.java b/common/src/java/com/example/android/common/midi/MidiPortConnector.java index 92517be0..457494d1 100644 --- a/common/src/java/com/example/android/common/midi/MidiPortConnector.java +++ b/common/src/java/com/example/android/common/midi/MidiPortConnector.java @@ -27,7 +27,7 @@ import android.util.Log; import java.io.IOException; /** - * Simple wrapper for connecting MIDI ports. + * Tool for connecting MIDI ports on two remote devices. */ public class MidiPortConnector { private final MidiManager mMidiManager; @@ -44,6 +44,8 @@ public class MidiPortConnector { public void close() throws IOException { if (mConnection != null) { + Log.i(MidiConstants.TAG, + "MidiPortConnector closing connection " + mConnection); mConnection.close(); mConnection = null; } @@ -57,23 +59,12 @@ public class MidiPortConnector { } } - /** - * @return a device that matches the manufacturer and product or null - */ - public MidiDeviceInfo findDevice(String manufacturer, String product) { - for (MidiDeviceInfo info : mMidiManager.getDevices()) { - String deviceManufacturer = info.getProperties() - .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER); - if ((manufacturer != null) - && manufacturer.equals(deviceManufacturer)) { - String deviceProduct = info.getProperties() - .getString(MidiDeviceInfo.PROPERTY_PRODUCT); - if ((product != null) && product.equals(deviceProduct)) { - return info; - } - } + private void safeClose() { + try { + close(); + } catch (IOException e) { + Log.e(MidiConstants.TAG, "could not close resources", e); } - return null; } /** @@ -120,35 +111,38 @@ public class MidiPortConnector { final MidiDeviceInfo destinationDeviceInfo, final int destinationPortIndex, final OnPortsConnectedListener listener, final Handler handler) { + safeClose(); mMidiManager.openDevice(destinationDeviceInfo, new MidiManager.OnDeviceOpenedListener() { @Override - public void onDeviceOpened(MidiDevice device) { - if (device == null) { + public void onDeviceOpened(MidiDevice destinationDevice) { + if (destinationDevice == null) { Log.e(MidiConstants.TAG, "could not open " + destinationDeviceInfo); if (listener != null) { listener.onPortsConnected(null); } } else { + mDestinationDevice = destinationDevice; Log.i(MidiConstants.TAG, "connectToDevicePort opened " + destinationDeviceInfo); // Destination device was opened so go to next step. - mDestinationDevice = device; - MidiInputPort destinationInputPort = device + MidiInputPort destinationInputPort = destinationDevice .openInputPort(destinationPortIndex); if (destinationInputPort != null) { Log.i(MidiConstants.TAG, "connectToDevicePort opened port on " + destinationDeviceInfo); connectToDevicePort(sourceDeviceInfo, - sourcePortIndex, destinationInputPort, + sourcePortIndex, + destinationInputPort, listener, handler); } else { Log.e(MidiConstants.TAG, "could not open port on " + destinationDeviceInfo); + safeClose(); if (listener != null) { listener.onPortsConnected(null); } @@ -158,20 +152,6 @@ public class MidiPortConnector { }, handler); } - /** - * Open a source device and connect its output port to the - * destinationInputPort. - * - * @param sourceDeviceInfo - * @param sourcePortIndex - * @param destinationInputPort - */ - public void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo, - final int sourcePortIndex, - final MidiInputPort destinationInputPort) { - connectToDevicePort(sourceDeviceInfo, sourcePortIndex, - destinationInputPort, null, null); - } /** * Open a source device and connect its output port to the @@ -181,8 +161,9 @@ public class MidiPortConnector { * @param sourcePortIndex * @param destinationInputPort */ - public void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo, - final int sourcePortIndex, final MidiInputPort destinationInputPort, + private void connectToDevicePort(final MidiDeviceInfo sourceDeviceInfo, + final int sourcePortIndex, + final MidiInputPort destinationInputPort, final OnPortsConnectedListener listener, final Handler handler) { mMidiManager.openDevice(sourceDeviceInfo, new MidiManager.OnDeviceOpenedListener() { @@ -191,6 +172,7 @@ public class MidiPortConnector { if (device == null) { Log.e(MidiConstants.TAG, "could not open " + sourceDeviceInfo); + safeClose(); if (listener != null) { listener.onPortsConnected(null); } @@ -205,6 +187,7 @@ public class MidiPortConnector { if (mConnection == null) { Log.e(MidiConstants.TAG, "could not connect to " + sourceDeviceInfo); + safeClose(); } if (listener != null) { listener.onPortsConnected(mConnection); diff --git a/common/src/java/com/example/android/common/midi/MidiPortSelector.java b/common/src/java/com/example/android/common/midi/MidiPortSelector.java index d491c037..39f983e3 100644 --- a/common/src/java/com/example/android/common/midi/MidiPortSelector.java +++ b/common/src/java/com/example/android/common/midi/MidiPortSelector.java @@ -18,35 +18,38 @@ package com.example.android.common.midi; import android.app.Activity; import android.media.midi.MidiDeviceInfo; +import android.media.midi.MidiDeviceStatus; import android.media.midi.MidiManager; import android.media.midi.MidiManager.DeviceCallback; import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; +import java.util.HashSet; + /** * Base class that uses a Spinner to select available MIDI ports. */ public abstract class MidiPortSelector extends DeviceCallback { - - public static final int TYPE_INPUT = 0; - public static final int TYPE_OUTPUT = 1; - private int mType = TYPE_INPUT; + private int mType = MidiDeviceInfo.PortInfo.TYPE_INPUT; protected ArrayAdapter<MidiPortWrapper> mAdapter; + protected HashSet<MidiPortWrapper> mBusyPorts = new HashSet<MidiPortWrapper>(); private Spinner mSpinner; protected MidiManager mMidiManager; protected Activity mActivity; private MidiPortWrapper mCurrentWrapper; /** - * * @param midiManager * @param activity - * @param spinnerId ID from the layout resource - * @param type TYPE_INPUT or TYPE_OUTPUT + * @param spinnerId + * ID from the layout resource + * @param type + * TYPE_INPUT or TYPE_OUTPUT */ public MidiPortSelector(MidiManager midiManager, Activity activity, int spinnerId, int type) { @@ -57,7 +60,7 @@ public abstract class MidiPortSelector extends DeviceCallback { android.R.layout.simple_spinner_item); mAdapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item); - mAdapter.add(new MidiPortWrapper(null, 0)); + mAdapter.add(new MidiPortWrapper(null, 0, 0)); mSpinner = (Spinner) activity.findViewById(spinnerId); mSpinner.setOnItemSelectedListener( @@ -85,9 +88,16 @@ public abstract class MidiPortSelector extends DeviceCallback { } } + /** + * Set to no port selected. + */ + public void clearSelection() { + mSpinner.setSelection(0); + } + private int getInfoPortCount(final MidiDeviceInfo info) { - int portCount = (mType == TYPE_INPUT) ? info.getInputPortCount() - : info.getOutputPortCount(); + int portCount = (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) + ? info.getInputPortCount() : info.getOutputPortCount(); return portCount; } @@ -95,7 +105,10 @@ public abstract class MidiPortSelector extends DeviceCallback { public void onDeviceAdded(final MidiDeviceInfo info) { int portCount = getInfoPortCount(info); for (int i = 0; i < portCount; ++i) { - mAdapter.add(new MidiPortWrapper(info, i)); + MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i); + mAdapter.add(wrapper); + Log.i(MidiConstants.TAG, wrapper + " was added"); + mAdapter.notifyDataSetChanged(); } } @@ -103,12 +116,48 @@ public abstract class MidiPortSelector extends DeviceCallback { public void onDeviceRemoved(final MidiDeviceInfo info) { int portCount = getInfoPortCount(info); for (int i = 0; i < portCount; ++i) { - MidiPortWrapper wrapper = new MidiPortWrapper(info, i); + MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i); MidiPortWrapper currentWrapper = mCurrentWrapper; mAdapter.remove(wrapper); // If the currently selected port was removed then select no port. if (wrapper.equals(currentWrapper)) { - mSpinner.setSelection(0); + clearSelection(); + } + mAdapter.notifyDataSetChanged(); + Log.i(MidiConstants.TAG, wrapper + " was removed"); + } + } + + @Override + public void onDeviceStatusChanged(final MidiDeviceStatus status) { + // If an input port becomes busy then remove it from the menu. + // If it becomes free then add it back to the menu. + if (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) { + MidiDeviceInfo info = status.getDeviceInfo(); + Log.i(MidiConstants.TAG, "MidiPortSelector.onDeviceStatusChanged status = " + status + + ", mType = " + mType + + ", activity = " + mActivity.getPackageName() + + ", info = " + info); + // Look for transitions from free to busy. + int portCount = info.getInputPortCount(); + for (int i = 0; i < portCount; ++i) { + MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i); + if (!wrapper.equals(mCurrentWrapper)) { + if (status.isInputPortOpen(i)) { // busy? + if (!mBusyPorts.contains(wrapper)) { + // was free, now busy + mBusyPorts.add(wrapper); + mAdapter.remove(wrapper); + mAdapter.notifyDataSetChanged(); + } + } else { + if (mBusyPorts.remove(wrapper)) { + // was busy, now free + mAdapter.add(wrapper); + mAdapter.notifyDataSetChanged(); + } + } + } } } } @@ -124,4 +173,11 @@ public abstract class MidiPortSelector extends DeviceCallback { * Implement this method to clean up any open resources. */ public abstract void onClose(); + + /** + * + */ + public void close() { + onClose(); + } } diff --git a/common/src/java/com/example/android/common/midi/MidiPortWrapper.java b/common/src/java/com/example/android/common/midi/MidiPortWrapper.java index 9f826947..77aa7345 100644 --- a/common/src/java/com/example/android/common/midi/MidiPortWrapper.java +++ b/common/src/java/com/example/android/common/midi/MidiPortWrapper.java @@ -18,16 +18,28 @@ package com.example.android.common.midi; import android.media.midi.MidiDeviceInfo; import android.media.midi.MidiDeviceInfo.PortInfo; +import android.util.Log; // Wrapper for a MIDI device and port description. public class MidiPortWrapper { private MidiDeviceInfo mInfo; private int mPortIndex; + private int mType; private String mString; - public MidiPortWrapper(MidiDeviceInfo info, int portIndex) { + /** + * Wrapper for a MIDI device and port description. + * @param info + * @param portType + * @param portIndex + */ + public MidiPortWrapper(MidiDeviceInfo info, int portType, int portIndex) { mInfo = info; + mType = portType; mPortIndex = portIndex; + } + + private void updateString() { if (mInfo == null) { mString = "- - - - - -"; } else { @@ -36,17 +48,39 @@ public class MidiPortWrapper { .getString(MidiDeviceInfo.PROPERTY_NAME); if (name == null) { name = mInfo.getProperties() - .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER) - + ", " + mInfo.getProperties() - .getString(MidiDeviceInfo.PROPERTY_PRODUCT); + .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER) + ", " + + mInfo.getProperties() + .getString(MidiDeviceInfo.PROPERTY_PRODUCT); + } + sb.append("#" + mInfo.getId()); + sb.append(", ").append(name); + PortInfo portInfo = findPortInfo(); + sb.append("[" + mPortIndex + "]"); + if (portInfo != null) { + sb.append(", ").append(portInfo.getName()); + } else { + sb.append(", null"); } - sb.append("#" + mInfo.getId()).append(", ").append(name); - PortInfo portInfo = mInfo.getPorts()[portIndex]; - sb.append(", ").append(portInfo.getName()); mString = sb.toString(); } } + /** + * @param info + * @param portIndex + * @return + */ + private PortInfo findPortInfo() { + PortInfo[] ports = mInfo.getPorts(); + for (PortInfo portInfo : ports) { + if (portInfo.getPortNumber() == mPortIndex + && portInfo.getType() == mType) { + return portInfo; + } + } + return null; + } + public int getPortIndex() { return mPortIndex; } @@ -57,6 +91,9 @@ public class MidiPortWrapper { @Override public String toString() { + if (mString == null) { + updateString(); + } return mString; } @@ -69,6 +106,8 @@ public class MidiPortWrapper { MidiPortWrapper otherWrapper = (MidiPortWrapper) other; if (mPortIndex != otherWrapper.mPortIndex) return false; + if (mType != otherWrapper.mType) + return false; if (mInfo == null) return (otherWrapper.mInfo == null); return mInfo.equals(otherWrapper.mInfo); @@ -76,7 +115,11 @@ public class MidiPortWrapper { @Override public int hashCode() { - return toString().hashCode(); + int hashCode = 1; + hashCode = 31 * hashCode + mPortIndex; + hashCode = 31 * hashCode + mType; + hashCode = 31 * hashCode + mInfo.hashCode(); + return hashCode; } } diff --git a/common/src/java/com/example/android/common/midi/MidiTools.java b/common/src/java/com/example/android/common/midi/MidiTools.java new file mode 100644 index 00000000..82e3de4b --- /dev/null +++ b/common/src/java/com/example/android/common/midi/MidiTools.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 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 com.example.android.common.midi; + +import android.media.midi.MidiDeviceInfo; +import android.media.midi.MidiManager; + +/** + * Miscellaneous tools for Android MIDI. + */ +public class MidiTools { + + /** + * @return a device that matches the manufacturer and product or null + */ + public static MidiDeviceInfo findDevice(MidiManager midiManager, + String manufacturer, String product) { + for (MidiDeviceInfo info : midiManager.getDevices()) { + String deviceManufacturer = info.getProperties() + .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER); + if ((manufacturer != null) + && manufacturer.equals(deviceManufacturer)) { + String deviceProduct = info.getProperties() + .getString(MidiDeviceInfo.PROPERTY_PRODUCT); + if ((product != null) && product.equals(deviceProduct)) { + return info; + } + } + } + return null; + } +} |