From c6475779990f6ffdfa33d42b0bd97818df5e8d3c Mon Sep 17 00:00:00 2001 From: toyoshim Date: Thu, 30 Apr 2015 07:52:02 -0700 Subject: Reland: Web MIDI: split build rules for media/midi Changes from the first coommit. - Define right dependencies to xdisplaycheck - Fix build rules for midi_unittests_apk to run on Android - Rebase error fixes (build rule deopendency adjustments) Original commit is: https://crrev.com/31624cad1a3b5cf26950b864837ba44b3aa38331 Original description follows: Split MIDI related stuffs from media as a component in build files. Also, MIDI related unit tests are split into midi_unittests. BUG=475869 TEST=git cl try TEST=ninja midi_unittests_apk && ./build/android/test_runner.py gtest midi_unittests Review URL: https://codereview.chromium.org/1065743003 Cr-Commit-Position: refs/heads/master@{#327712} --- media/BUILD.gn | 65 +--- media/OWNERS | 5 - media/base/android/BUILD.gn | 2 - .../org/chromium/media/UsbMidiDeviceAndroid.java | 327 --------------------- .../media/UsbMidiDeviceFactoryAndroid.java | 264 ----------------- media/base/android/media_jni_registrar.cc | 6 - media/media.gyp | 60 ---- media/midi/BUILD.gn | 172 +++++++++++ .../chromium/media/midi/UsbMidiDeviceAndroid.java | 326 ++++++++++++++++++++ .../media/midi/UsbMidiDeviceFactoryAndroid.java | 264 +++++++++++++++++ media/midi/midi.gyp | 235 +++++++++++++++ media/midi/midi_jni_registrar.cc | 29 ++ media/midi/midi_jni_registrar.h | 21 ++ media/midi/midi_manager_usb.cc | 10 +- media/midi/midi_manager_win.cc | 6 +- media/midi/midi_scheduler.h | 3 +- media/midi/midi_unittests.isolate | 63 ++++ media/midi/usb_midi_device_android.cc | 18 +- media/midi/usb_midi_device_factory_android.cc | 8 +- media/midi/usb_midi_output_stream.cc | 2 +- 20 files changed, 1136 insertions(+), 750 deletions(-) delete mode 100644 media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java delete mode 100644 media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java create mode 100644 media/midi/BUILD.gn create mode 100644 media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceAndroid.java create mode 100644 media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceFactoryAndroid.java create mode 100644 media/midi/midi.gyp create mode 100644 media/midi/midi_jni_registrar.cc create mode 100644 media/midi/midi_jni_registrar.h create mode 100644 media/midi/midi_unittests.isolate (limited to 'media') diff --git a/media/BUILD.gn b/media/BUILD.gn index 4c8d52a..b62b0ea 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -32,9 +32,6 @@ config("media_config") { if (use_cras) { defines += [ "USE_CRAS" ] } - if (use_alsa) { - defines += [ "USE_ALSA" ] - } } config("media_implementation") { @@ -178,30 +175,6 @@ component("media") { "formats/webm/webm_video_client.h", "formats/webm/webm_webvtt_parser.cc", "formats/webm/webm_webvtt_parser.h", - "midi/midi_manager.cc", - "midi/midi_manager.h", - "midi/midi_manager_mac.cc", - "midi/midi_manager_mac.h", - "midi/midi_manager_usb.cc", - "midi/midi_manager_usb.h", - "midi/midi_manager_win.cc", - "midi/midi_manager_win.h", - "midi/midi_message_queue.cc", - "midi/midi_message_queue.h", - "midi/midi_message_util.cc", - "midi/midi_message_util.h", - "midi/midi_port_info.cc", - "midi/midi_port_info.h", - "midi/midi_scheduler.cc", - "midi/midi_scheduler.h", - "midi/usb_midi_descriptor_parser.cc", - "midi/usb_midi_descriptor_parser.h", - "midi/usb_midi_device.h", - "midi/usb_midi_input_stream.cc", - "midi/usb_midi_input_stream.h", - "midi/usb_midi_jack.h", - "midi/usb_midi_output_stream.cc", - "midi/usb_midi_output_stream.h", "renderers/audio_renderer_impl.cc", "renderers/audio_renderer_impl.h", "renderers/gpu_video_accelerator_factories.h", @@ -337,11 +310,6 @@ component("media") { if (is_android) { sources += [ - "midi/midi_manager_android.cc", - "midi/usb_midi_device_android.cc", - "midi/usb_midi_device_android.h", - "midi/usb_midi_device_factory_android.cc", - "midi/usb_midi_device_factory_android.h", "video/capture/android/video_capture_device_android.cc", "video/capture/android/video_capture_device_android.h", "video/capture/android/video_capture_device_factory_android.cc", @@ -370,15 +338,6 @@ component("media") { ] } - if (use_alsa && use_udev) { - libs += [ "asound" ] - sources += [ - "midi/midi_manager_alsa.cc", - "midi/midi_manager_alsa.h", - ] - deps += [ "//device/udev_linux" ] - } - # A simple WebM encoder for animated avatars on ChromeOS. if (use_ozone) { @@ -402,7 +361,6 @@ component("media") { "//third_party/decklink", ] libs += [ - "CoreMIDI.framework", "CoreVideo.framework", "OpenGL.framework", "QTKit.framework", @@ -421,8 +379,6 @@ component("media") { } if (is_win) { - deps += [ "//device/usb" ] - libs += [ "mf.lib", "mfplat.lib", @@ -575,13 +531,6 @@ test("media_unittests") { "formats/webm/webm_parser_unittest.cc", "formats/webm/webm_tracks_parser_unittest.cc", "formats/webm/webm_webvtt_parser_unittest.cc", - "midi/midi_manager_unittest.cc", - "midi/midi_manager_usb_unittest.cc", - "midi/midi_message_queue_unittest.cc", - "midi/midi_message_util_unittest.cc", - "midi/usb_midi_descriptor_parser_unittest.cc", - "midi/usb_midi_input_stream_unittest.cc", - "midi/usb_midi_output_stream_unittest.cc", "renderers/audio_renderer_impl_unittest.cc", "renderers/renderer_impl_unittest.cc", "renderers/video_renderer_impl_unittest.cc", @@ -669,18 +618,8 @@ test("media_unittests") { } if (is_mac) { - sources += [ - "midi/midi_manager_mac_unittest.cc", - "video/capture/mac/video_capture_device_factory_mac_unittest.mm", - ] - libs = [ - # Required by midi_manager_mac_unittest.cc. - "CoreMIDI.framework", - ] - } - - if (use_alsa && use_udev) { - sources += [ "midi/midi_manager_alsa_unittest.cc" ] + sources += + [ "video/capture/mac/video_capture_device_factory_mac_unittest.mm" ] } # include_dirs += [ diff --git a/media/OWNERS b/media/OWNERS index b7c2a18..fdb8c86 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -6,10 +6,5 @@ scherkus@chromium.org wolenetz@chromium.org xhwang@chromium.org -# Build rules for Web MIDI -per-file BUILD.gn=toyoshim@chromium.org -per-file *.gyp=toyoshim@chromium.org -per-file *.gypi=toyoshim@chromium.org - per-file *.isolate=csharp@chromium.org per-file *.isolate=maruel@chromium.org diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn index c407b17..9ab5265 100644 --- a/media/base/android/BUILD.gn +++ b/media/base/android/BUILD.gn @@ -87,8 +87,6 @@ generate_jni("media_jni_headers") { "java/src/org/chromium/media/MediaDrmBridge.java", "java/src/org/chromium/media/MediaPlayerBridge.java", "java/src/org/chromium/media/MediaPlayerListener.java", - "java/src/org/chromium/media/UsbMidiDeviceAndroid.java", - "java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java", "java/src/org/chromium/media/WebAudioMediaCodecBridge.java", ] jni_package = "media" diff --git a/media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java b/media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java deleted file mode 100644 index c581131..0000000 --- a/media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.annotation.TargetApi; -import android.hardware.usb.UsbConstants; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; -import android.hardware.usb.UsbEndpoint; -import android.hardware.usb.UsbInterface; -import android.hardware.usb.UsbManager; -import android.hardware.usb.UsbRequest; -import android.os.Build; -import android.os.Handler; -import android.util.SparseArray; - -import org.chromium.base.CalledByNative; -import org.chromium.base.JNINamespace; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * Owned by its native counterpart declared in usb_midi_device_android.h. - * Refer to that class for general comments. - */ -@JNINamespace("media") -class UsbMidiDeviceAndroid { - /** - * A connection handle for this device. - */ - private final UsbDeviceConnection mConnection; - - /** - * A map from endpoint number to UsbEndpoint. - */ - private final SparseArray mEndpointMap; - - /** - * A map from UsbEndpoint to UsbRequest associated to it. - */ - private final Map mRequestMap; - - /** - * The handler used for posting events on the main thread. - */ - private final Handler mHandler; - - /** - * True if this device is closed. - */ - private boolean mIsClosed; - - /** - * True if there is a thread processing input data. - */ - private boolean mHasInputThread; - - /** - * The identifier of this device. - */ - private long mNativePointer; - - /** - * The underlying USB device. - */ - private UsbDevice mUsbDevice; - - /** - * Audio interface subclass code for MIDI. - */ - static final int MIDI_SUBCLASS = 3; - - /** - * The request type to request a USB descriptor. - */ - static final int REQUEST_GET_DESCRIPTOR = 0x06; - - /** - * The STRING descriptor type. - */ - static final int STRING_DESCRIPTOR_TYPE = 0x03; - - /** - * Constructs a UsbMidiDeviceAndroid. - * @param manager - * @param device The USB device which this object is assocated with. - */ - UsbMidiDeviceAndroid(UsbManager manager, UsbDevice device) { - mConnection = manager.openDevice(device); - mEndpointMap = new SparseArray(); - mRequestMap = new HashMap(); - mHandler = new Handler(); - mUsbDevice = device; - mIsClosed = false; - mHasInputThread = false; - mNativePointer = 0; - - for (int i = 0; i < device.getInterfaceCount(); ++i) { - UsbInterface iface = device.getInterface(i); - if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO - || iface.getInterfaceSubclass() != MIDI_SUBCLASS) { - continue; - } - mConnection.claimInterface(iface, true); - for (int j = 0; j < iface.getEndpointCount(); ++j) { - UsbEndpoint endpoint = iface.getEndpoint(j); - if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) { - mEndpointMap.put(endpoint.getEndpointNumber(), endpoint); - } - } - } - // Start listening for input endpoints. - // This function will create and run a thread if there is USB-MIDI endpoints in the - // device. Note that because UsbMidiDevice is shared among all tabs and the thread - // will be terminated when the device is disconnected, at most one thread can be created - // for each connected USB-MIDI device. - startListen(device); - } - - /** - * Starts listening for input endpoints. - */ - private void startListen(final UsbDevice device) { - final Map bufferForEndpoints = - new HashMap(); - - for (int i = 0; i < device.getInterfaceCount(); ++i) { - UsbInterface iface = device.getInterface(i); - if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO - || iface.getInterfaceSubclass() != MIDI_SUBCLASS) { - continue; - } - for (int j = 0; j < iface.getEndpointCount(); ++j) { - UsbEndpoint endpoint = iface.getEndpoint(j); - if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) { - ByteBuffer buffer = ByteBuffer.allocate(endpoint.getMaxPacketSize()); - UsbRequest request = new UsbRequest(); - request.initialize(mConnection, endpoint); - request.queue(buffer, buffer.remaining()); - bufferForEndpoints.put(endpoint, buffer); - } - } - } - if (bufferForEndpoints.isEmpty()) { - return; - } - mHasInputThread = true; - // bufferForEndpoints must not be accessed hereafter on this thread. - new Thread() { - @Override - public void run() { - while (true) { - UsbRequest request = mConnection.requestWait(); - if (request == null) { - // When the device is closed requestWait will fail. - break; - } - UsbEndpoint endpoint = request.getEndpoint(); - if (endpoint.getDirection() != UsbConstants.USB_DIR_IN) { - continue; - } - ByteBuffer buffer = bufferForEndpoints.get(endpoint); - int length = getInputDataLength(buffer); - if (length > 0) { - buffer.rewind(); - final byte[] bs = new byte[length]; - buffer.get(bs, 0, length); - postOnDataEvent(endpoint.getEndpointNumber(), bs); - } - buffer.rewind(); - request.queue(buffer, buffer.capacity()); - } - } - }.start(); - } - - /** - * Posts a data input event to the main thread. - */ - private void postOnDataEvent(final int endpointNumber, final byte[] bs) { - mHandler.post(new Runnable() { - @Override - public void run() { - if (mIsClosed) { - return; - } - nativeOnData(mNativePointer, endpointNumber, bs); - } - }); - } - - UsbDevice getUsbDevice() { - return mUsbDevice; - } - - boolean isClosed() { - return mIsClosed; - } - - /** - * Register the own native pointer. - */ - @CalledByNative - void registerSelf(long nativePointer) { - mNativePointer = nativePointer; - } - - /** - * Sends a USB-MIDI data to the device. - * @param endpointNumber The endpoint number of the destination endpoint. - * @param bs The data to be sent. - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) - @CalledByNative - void send(int endpointNumber, byte[] bs) { - if (mIsClosed) { - return; - } - UsbEndpoint endpoint = mEndpointMap.get(endpointNumber); - if (endpoint == null) { - return; - } - if (shouldUseBulkTransfer()) { - // We use bulkTransfer instead of UsbRequest.queue because queueing - // a UsbRequest is currently not thread safe. - // Note that this is not exactly correct because we don't care - // about the transfer attribute (bmAttribute) of the endpoint. - // See also: - // http://stackoverflow.com/questions/9644415/ - // https://code.google.com/p/android/issues/detail?id=59467 - // - // TODO(yhirano): Delete this block once the problem is fixed. - final int timeout = 100; - mConnection.bulkTransfer(endpoint, bs, bs.length, timeout); - } else { - UsbRequest request = mRequestMap.get(endpoint); - if (request == null) { - request = new UsbRequest(); - request.initialize(mConnection, endpoint); - mRequestMap.put(endpoint, request); - } - request.queue(ByteBuffer.wrap(bs), bs.length); - } - } - - /** - * Returns true if |bulkTransfer| should be used in |send|. - * See comments in |send|. - */ - private boolean shouldUseBulkTransfer() { - return mHasInputThread; - } - - /** - * Returns the descriptors bytes of this device. - * @return The descriptors bytes of this device. - */ - @CalledByNative - byte[] getDescriptors() { - if (mConnection == null) { - return new byte[0]; - } - return mConnection.getRawDescriptors(); - } - - /** - * Returns the string descriptor bytes for the given index - * @param index index of the descriptor - * @return the string descriptor bytes for the given index. - */ - @CalledByNative - byte[] getStringDescriptor(int index) { - if (mConnection == null) { - return new byte[0]; - } - byte[] buffer = new byte[255]; - int type = UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD; - int request = REQUEST_GET_DESCRIPTOR; - int value = (STRING_DESCRIPTOR_TYPE << 8) | index; - int read = mConnection.controlTransfer(type, request, value, 0, buffer, buffer.length, 0); - if (read < 0) { - return new byte[0]; - } - return Arrays.copyOf(buffer, read); - } - - /** - * Closes the device connection. - */ - @CalledByNative - void close() { - mEndpointMap.clear(); - for (UsbRequest request : mRequestMap.values()) { - request.close(); - } - mRequestMap.clear(); - mConnection.close(); - mNativePointer = 0; - mIsClosed = true; - } - - /** - * Returns the length of a USB-MIDI input. - * Since the Android API doesn't provide us the length, - * we calculate it manually. - */ - private static int getInputDataLength(ByteBuffer buffer) { - int position = buffer.position(); - // We assume that the data length is always divisable by 4. - for (int i = 0; i < position; i += 4) { - // Since Code Index Number 0 is reserved, it is not a valid USB-MIDI data. - if (buffer.get(i) == 0) { - return i; - } - } - return position; - } - - private static native void nativeOnData(long nativeUsbMidiDeviceAndroid, - int endpointNumber, - byte[] data); -} diff --git a/media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java b/media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java deleted file mode 100644 index 814bb17..0000000 --- a/media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.hardware.usb.UsbConstants; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbInterface; -import android.hardware.usb.UsbManager; -import android.os.Parcelable; - -import org.chromium.base.CalledByNative; -import org.chromium.base.JNINamespace; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Owned by its native counterpart declared in - * usb_midi_device_factory_android.h. Refer to that class for general comments. - */ -@JNINamespace("media") -class UsbMidiDeviceFactoryAndroid { - /** - * The UsbManager of this system. - */ - private UsbManager mUsbManager; - - /** - * A BroadcastReceiver for USB device events. - */ - private BroadcastReceiver mReceiver; - - /** - * Accessible USB-MIDI devices got so far. - */ - private final List mDevices = new ArrayList(); - - /** - * Devices whose access permission requested but not resolved so far. - */ - private Set mRequestedDevices; - - /** - * True when the enumeration is in progress. - */ - private boolean mIsEnumeratingDevices; - - /** - * The identifier of this factory. - */ - private long mNativePointer; - - private static final String ACTION_USB_PERMISSION = - "org.chromium.media.USB_PERMISSION"; - - /** - * Constructs a UsbMidiDeviceAndroid. - * @param context - * @param nativePointer The native pointer to which the created factory is associated. - */ - UsbMidiDeviceFactoryAndroid(Context context, long nativePointer) { - mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); - mNativePointer = nativePointer; - mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Parcelable extra = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); - if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { - requestDevicePermissionIfNecessary(context, (UsbDevice) extra); - } - if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { - onUsbDeviceDetached((UsbDevice) extra); - } - if (ACTION_USB_PERMISSION.equals(intent.getAction())) { - onUsbDevicePermissionRequestDone(context, intent); - } - } - }; - IntentFilter filter = new IntentFilter(); - filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); - filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); - filter.addAction(ACTION_USB_PERMISSION); - context.registerReceiver(mReceiver, filter); - mRequestedDevices = new HashSet(); - } - - /** - * Constructs a UsbMidiDeviceAndroid. - * @param context - * @param nativePointer The native pointer to which the created factory is associated. - */ - @CalledByNative - static UsbMidiDeviceFactoryAndroid create(Context context, long nativePointer) { - return new UsbMidiDeviceFactoryAndroid(context, nativePointer); - } - - /** - * Enumerates USB-MIDI devices. - * If there are devices having USB-MIDI interfaces, this function requests permission for - * accessing the device to the user. - * When the permission request is accepted or rejected, nativeOnUsbMidiDeviceRequestDone - * will be called. - * - * If there are no USB-MIDI interfaces, this function returns false. - * @param context - * @return true if some permission requests are in progress. - */ - @CalledByNative - boolean enumerateDevices(Context context) { - assert !mIsEnumeratingDevices; - mIsEnumeratingDevices = true; - Map devices = mUsbManager.getDeviceList(); - if (devices.isEmpty()) { - // No USB-MIDI devices are found. - mIsEnumeratingDevices = false; - return false; - } - for (UsbDevice device: devices.values()) { - requestDevicePermissionIfNecessary(context, device); - } - return true; - } - - /** - * Request a device access permission if there is a MIDI interface in the device. - * - * @param context - * @param device a USB device - */ - private void requestDevicePermissionIfNecessary(Context context, UsbDevice device) { - for (UsbDevice d: mRequestedDevices) { - if (d.getDeviceId() == device.getDeviceId()) { - // It is already requested. - return; - } - } - - for (int i = 0; i < device.getInterfaceCount(); ++i) { - UsbInterface iface = device.getInterface(i); - if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO - && iface.getInterfaceSubclass() == UsbMidiDeviceAndroid.MIDI_SUBCLASS) { - // There is at least one interface supporting MIDI. - mUsbManager.requestPermission(device, PendingIntent.getBroadcast( - context, 0, new Intent(ACTION_USB_PERMISSION), 0)); - mRequestedDevices.add(device); - break; - } - } - } - - /** - * Called when a USB device is detached. - * - * @param device a USB device - */ - private void onUsbDeviceDetached(UsbDevice device) { - for (UsbDevice usbDevice: mRequestedDevices) { - if (usbDevice.getDeviceId() == device.getDeviceId()) { - mRequestedDevices.remove(usbDevice); - break; - } - } - for (int i = 0; i < mDevices.size(); ++i) { - UsbMidiDeviceAndroid midiDevice = mDevices.get(i); - if (midiDevice.isClosed()) { - // Once a device is disconnected, the system may reassign its device ID to - // another device. So we should ignore disconnected ones. - continue; - } - if (midiDevice.getUsbDevice().getDeviceId() == device.getDeviceId()) { - midiDevice.close(); - if (mIsEnumeratingDevices) { - // In this case, we don't have to keep mDevices sync with the devices list - // in MidiManagerUsb. - mDevices.remove(i); - return; - } - if (mNativePointer != 0) { - nativeOnUsbMidiDeviceDetached(mNativePointer, i); - } - return; - } - } - } - - /** - * Called when the user accepts or rejects the permission request requested by - * EnumerateDevices. - * - * @param context - * @param intent - */ - private void onUsbDevicePermissionRequestDone(Context context, Intent intent) { - UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); - UsbMidiDeviceAndroid midiDevice = null; - if (mRequestedDevices.contains(device)) { - mRequestedDevices.remove(device); - if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { - // The request was rejected. - device = null; - } - } else { - device = null; - } - - if (device != null) { - for (UsbMidiDeviceAndroid registered: mDevices) { - if (!registered.isClosed() - && registered.getUsbDevice().getDeviceId() == device.getDeviceId()) { - // The device is already registered. - device = null; - break; - } - } - } - - if (device != null) { - // Now we can add the device. - midiDevice = new UsbMidiDeviceAndroid(mUsbManager, device); - mDevices.add(midiDevice); - } - - if (!mRequestedDevices.isEmpty()) { - return; - } - if (mNativePointer == 0) { - return; - } - - if (mIsEnumeratingDevices) { - nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray()); - mIsEnumeratingDevices = false; - } else if (midiDevice != null) { - nativeOnUsbMidiDeviceAttached(mNativePointer, midiDevice); - } - } - - /** - * Disconnects the native object. - * @param context - */ - @CalledByNative - void close(Context context) { - mNativePointer = 0; - context.unregisterReceiver(mReceiver); - } - - private static native void nativeOnUsbMidiDeviceRequestDone( - long nativeUsbMidiDeviceFactoryAndroid, Object[] devices); - private static native void nativeOnUsbMidiDeviceAttached( - long nativeUsbMidiDeviceFactoryAndroid, Object device); - private static native void nativeOnUsbMidiDeviceDetached( - long nativeUsbMidiDeviceFactoryAndroid, int index); -} diff --git a/media/base/android/media_jni_registrar.cc b/media/base/android/media_jni_registrar.cc index c9b8489..9ab40e4 100644 --- a/media/base/android/media_jni_registrar.cc +++ b/media/base/android/media_jni_registrar.cc @@ -15,8 +15,6 @@ #include "media/base/android/media_player_bridge.h" #include "media/base/android/media_player_listener.h" #include "media/base/android/webaudio_media_codec_bridge.h" -#include "media/midi/usb_midi_device_android.h" -#include "media/midi/usb_midi_device_factory_android.h" #include "media/video/capture/android/video_capture_device_android.h" #include "media/video/capture/android/video_capture_device_factory_android.h" @@ -35,10 +33,6 @@ static base::android::RegistrationMethod kMediaRegisteredMethods[] = { MediaPlayerBridge::RegisterMediaPlayerBridge }, { "MediaPlayerListener", MediaPlayerListener::RegisterMediaPlayerListener }, - { "UsbMidiDevice", - UsbMidiDeviceAndroid::RegisterUsbMidiDevice }, - { "UsbMidiDeviceFactory", - UsbMidiDeviceFactoryAndroid::RegisterUsbMidiDeviceFactory }, { "VideoCaptureDevice", VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice }, { "VideoCaptureDeviceFactory", diff --git a/media/media.gyp b/media/media.gyp index c228c83..dac02a3 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -512,37 +512,6 @@ 'formats/webm/webm_video_client.cc', 'formats/webm/webm_video_client.h', 'formats/webm/webm_webvtt_parser.cc', - 'midi/midi_manager.cc', - 'midi/midi_manager.h', - 'midi/midi_manager_alsa.cc', - 'midi/midi_manager_alsa.h', - 'midi/midi_manager_android.cc', - 'midi/midi_manager_mac.cc', - 'midi/midi_manager_mac.h', - 'midi/midi_manager_usb.cc', - 'midi/midi_manager_usb.h', - 'midi/midi_manager_win.cc', - 'midi/midi_manager_win.h', - 'midi/midi_message_queue.cc', - 'midi/midi_message_queue.h', - 'midi/midi_message_util.cc', - 'midi/midi_message_util.h', - 'midi/midi_port_info.cc', - 'midi/midi_port_info.h', - 'midi/midi_scheduler.cc', - 'midi/midi_scheduler.h', - 'midi/usb_midi_descriptor_parser.cc', - 'midi/usb_midi_descriptor_parser.h', - 'midi/usb_midi_device.h', - 'midi/usb_midi_device_android.cc', - 'midi/usb_midi_device_android.h', - 'midi/usb_midi_device_factory_android.cc', - 'midi/usb_midi_device_factory_android.h', - 'midi/usb_midi_input_stream.cc', - 'midi/usb_midi_input_stream.h', - 'midi/usb_midi_jack.h', - 'midi/usb_midi_output_stream.cc', - 'midi/usb_midi_output_stream.h', 'ozone/media_ozone_platform.cc', 'ozone/media_ozone_platform.h', 'renderers/audio_renderer_impl.cc', @@ -956,7 +925,6 @@ '$(SDKROOT)/System/Library/Frameworks/AudioToolbox.framework', '$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework', '$(SDKROOT)/System/Library/Frameworks/CoreAudio.framework', - '$(SDKROOT)/System/Library/Frameworks/CoreMIDI.framework', '$(SDKROOT)/System/Library/Frameworks/CoreVideo.framework', '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', '$(SDKROOT)/System/Library/Frameworks/QTKit.framework', @@ -964,9 +932,6 @@ }, }], ['OS=="win"', { - 'dependencies': [ - '../device/usb/usb.gyp:device_usb', - ], 'link_settings': { 'libraries': [ '-lmf.lib', @@ -1093,16 +1058,6 @@ 'filters/source_buffer_platform.h', ] }], - ['use_udev==1', { - 'dependencies': [ - '../device/udev_linux/udev.gyp:udev_linux', - ], - }, { # use_udev==0 - 'sources!': [ - 'midi/midi_manager_alsa.cc', - 'midi/midi_manager_alsa.h', - ], - }], ], # conditions 'target_conditions': [ ['OS == "ios" and _toolset != "host"', { @@ -1274,13 +1229,6 @@ 'formats/webm/webm_parser_unittest.cc', 'formats/webm/webm_tracks_parser_unittest.cc', 'formats/webm/webm_webvtt_parser_unittest.cc', - 'midi/midi_manager_unittest.cc', - 'midi/midi_manager_usb_unittest.cc', - 'midi/midi_message_queue_unittest.cc', - 'midi/midi_message_util_unittest.cc', - 'midi/usb_midi_descriptor_parser_unittest.cc', - 'midi/usb_midi_input_stream_unittest.cc', - 'midi/usb_midi_output_stream_unittest.cc', 'renderers/audio_renderer_impl_unittest.cc', 'renderers/renderer_impl_unittest.cc', 'renderers/video_renderer_impl_unittest.cc', @@ -1413,15 +1361,9 @@ }], ['OS=="mac"', { 'sources': [ - 'midi/midi_manager_mac_unittest.cc', 'video/capture/mac/video_capture_device_factory_mac_unittest.mm', ] }], - ['use_alsa==1 and use_udev==1', { - 'sources': [ - 'midi/midi_manager_alsa_unittest.cc', - ] - }], ['use_x11==1', { 'dependencies': [ '../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck', @@ -1705,8 +1647,6 @@ 'base/android/java/src/org/chromium/media/MediaDrmBridge.java', 'base/android/java/src/org/chromium/media/MediaPlayerBridge.java', 'base/android/java/src/org/chromium/media/MediaPlayerListener.java', - 'base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java', - 'base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java', 'base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java', ], 'variables': { diff --git a/media/midi/BUILD.gn b/media/midi/BUILD.gn new file mode 100644 index 0000000..0faa867 --- /dev/null +++ b/media/midi/BUILD.gn @@ -0,0 +1,172 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/config.gni") +import("//build/config/features.gni") +import("//build/config/ui.gni") +import("//media/media_options.gni") +import("//testing/test.gni") + +if (is_android) { + # For generate_jni(). + import("//build/config/android/rules.gni") +} + +# Common configuration for targets in the media/midi directory. +config("midi_config") { + if (use_alsa) { + defines = [ + "USE_ALSA", + "USE_UDEV", + ] + } + + visibility = [ ":*" ] +} + +# USB MIDI specific source files that are used in Android port, and unit tests. +source_set("usb_midi") { + sources = [ + "midi_manager_usb.cc", + "midi_manager_usb.h", + "usb_midi_descriptor_parser.cc", + "usb_midi_descriptor_parser.h", + "usb_midi_device.h", + "usb_midi_input_stream.cc", + "usb_midi_input_stream.h", + "usb_midi_jack.h", + "usb_midi_output_stream.cc", + "usb_midi_output_stream.h", + ] + + defines = [ "MEDIA_IMPLEMENTATION" ] + deps = [ + "//base", + ] +} + +# Android specific Java source files that are used to generate jni header files. +if (is_android) { + android_library("midi_java") { + deps = [ + "//base:base_java", + ] + + DEPRECATED_java_in_dir = "java/src" + } + + generate_jni("midi_jni_headers") { + sources = [ + "java/src/org/chromium/media/midi/UsbMidiDeviceAndroid.java", + "java/src/org/chromium/media/midi/UsbMidiDeviceFactoryAndroid.java", + ] + jni_package = "media/midi" + } +} + +component("midi") { + sources = [ + "midi_manager.cc", + "midi_manager.h", + "midi_manager_mac.cc", + "midi_manager_mac.h", + "midi_manager_win.cc", + "midi_manager_win.h", + "midi_message_queue.cc", + "midi_message_queue.h", + "midi_message_util.cc", + "midi_message_util.h", + "midi_port_info.cc", + "midi_port_info.h", + "midi_scheduler.cc", + "midi_scheduler.h", + ] + + configs += [ ":midi_config" ] + + defines = [ "MEDIA_IMPLEMENTATION" ] + deps = [] + libs = [] + + if (is_android) { + sources += [ + "midi_jni_registrar.cc", + "midi_jni_registrar.h", + "midi_manager_android.cc", + "usb_midi_device_android.cc", + "usb_midi_device_android.h", + "usb_midi_device_factory_android.cc", + "usb_midi_device_factory_android.h", + ] + + deps += [ + "//base:i18n", + ":midi_jni_headers", + ":usb_midi", + ] + } + + if (is_mac) { + libs += [ + "CoreAudio.framework", + "CoreMIDI.framework", + ] + } + + if (is_win) { + deps += [ "//device/usb" ] + } + + if (use_alsa && use_udev) { + deps += [ + "//crypto", + "//crypto:platform", + ] + libs += [ "asound" ] + sources += [ + "midi_manager_alsa.cc", + "midi_manager_alsa.h", + ] + } + + if (use_udev) { + deps += [ "//device/udev_linux" ] + } +} + +test("midi_unittests") { + sources = [ + "midi_manager_unittest.cc", + "midi_manager_usb_unittest.cc", + "midi_message_queue_unittest.cc", + "midi_message_util_unittest.cc", + "usb_midi_descriptor_parser_unittest.cc", + "usb_midi_input_stream_unittest.cc", + "usb_midi_output_stream_unittest.cc", + ] + + configs += [ ":midi_config" ] + deps = [ + ":midi", + "//base/test/:run_all_unittests", + "//base/test/:test_support", + "//testing/gtest", + ] + + if (!is_android) { + deps += [ ":usb_midi" ] + } + + if (is_mac) { + sources += [ "midi_manager_mac_unittest.cc" ] + } + + if (use_alsa && use_udev) { + sources += [ "midi_manager_alsa_unittest.cc" ] + } + + if (use_x11) { + deps += [ "//tools/xdisplaycheck" ] + } +} diff --git a/media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceAndroid.java b/media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceAndroid.java new file mode 100644 index 0000000..4bdd5f5 --- /dev/null +++ b/media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceAndroid.java @@ -0,0 +1,326 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.media.midi; + +import android.annotation.TargetApi; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; +import android.hardware.usb.UsbRequest; +import android.os.Build; +import android.os.Handler; +import android.util.SparseArray; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Owned by its native counterpart declared in usb_midi_device_android.h. + * Refer to that class for general comments. + */ +@JNINamespace("media::midi") +class UsbMidiDeviceAndroid { + /** + * A connection handle for this device. + */ + private final UsbDeviceConnection mConnection; + + /** + * A map from endpoint number to UsbEndpoint. + */ + private final SparseArray mEndpointMap; + + /** + * A map from UsbEndpoint to UsbRequest associated to it. + */ + private final Map mRequestMap; + + /** + * The handler used for posting events on the main thread. + */ + private final Handler mHandler; + + /** + * True if this device is closed. + */ + private boolean mIsClosed; + + /** + * True if there is a thread processing input data. + */ + private boolean mHasInputThread; + + /** + * The identifier of this device. + */ + private long mNativePointer; + + /** + * The underlying USB device. + */ + private UsbDevice mUsbDevice; + + /** + * Audio interface subclass code for MIDI. + */ + static final int MIDI_SUBCLASS = 3; + + /** + * The request type to request a USB descriptor. + */ + static final int REQUEST_GET_DESCRIPTOR = 0x06; + + /** + * The STRING descriptor type. + */ + static final int STRING_DESCRIPTOR_TYPE = 0x03; + + /** + * Constructs a UsbMidiDeviceAndroid. + * @param manager + * @param device The USB device which this object is assocated with. + */ + UsbMidiDeviceAndroid(UsbManager manager, UsbDevice device) { + mConnection = manager.openDevice(device); + mEndpointMap = new SparseArray(); + mRequestMap = new HashMap(); + mHandler = new Handler(); + mUsbDevice = device; + mIsClosed = false; + mHasInputThread = false; + mNativePointer = 0; + + for (int i = 0; i < device.getInterfaceCount(); ++i) { + UsbInterface iface = device.getInterface(i); + if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO + || iface.getInterfaceSubclass() != MIDI_SUBCLASS) { + continue; + } + mConnection.claimInterface(iface, true); + for (int j = 0; j < iface.getEndpointCount(); ++j) { + UsbEndpoint endpoint = iface.getEndpoint(j); + if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) { + mEndpointMap.put(endpoint.getEndpointNumber(), endpoint); + } + } + } + // Start listening for input endpoints. + // This function will create and run a thread if there is USB-MIDI endpoints in the + // device. Note that because UsbMidiDevice is shared among all tabs and the thread + // will be terminated when the device is disconnected, at most one thread can be created + // for each connected USB-MIDI device. + startListen(device); + } + + /** + * Starts listening for input endpoints. + */ + private void startListen(final UsbDevice device) { + final Map bufferForEndpoints = + new HashMap(); + + for (int i = 0; i < device.getInterfaceCount(); ++i) { + UsbInterface iface = device.getInterface(i); + if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO + || iface.getInterfaceSubclass() != MIDI_SUBCLASS) { + continue; + } + for (int j = 0; j < iface.getEndpointCount(); ++j) { + UsbEndpoint endpoint = iface.getEndpoint(j); + if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) { + ByteBuffer buffer = ByteBuffer.allocate(endpoint.getMaxPacketSize()); + UsbRequest request = new UsbRequest(); + request.initialize(mConnection, endpoint); + request.queue(buffer, buffer.remaining()); + bufferForEndpoints.put(endpoint, buffer); + } + } + } + if (bufferForEndpoints.isEmpty()) { + return; + } + mHasInputThread = true; + // bufferForEndpoints must not be accessed hereafter on this thread. + new Thread() { + @Override + public void run() { + while (true) { + UsbRequest request = mConnection.requestWait(); + if (request == null) { + // When the device is closed requestWait will fail. + break; + } + UsbEndpoint endpoint = request.getEndpoint(); + if (endpoint.getDirection() != UsbConstants.USB_DIR_IN) { + continue; + } + ByteBuffer buffer = bufferForEndpoints.get(endpoint); + int length = getInputDataLength(buffer); + if (length > 0) { + buffer.rewind(); + final byte[] bs = new byte[length]; + buffer.get(bs, 0, length); + postOnDataEvent(endpoint.getEndpointNumber(), bs); + } + buffer.rewind(); + request.queue(buffer, buffer.capacity()); + } + } + }.start(); + } + + /** + * Posts a data input event to the main thread. + */ + private void postOnDataEvent(final int endpointNumber, final byte[] bs) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mIsClosed) { + return; + } + nativeOnData(mNativePointer, endpointNumber, bs); + } + }); + } + + UsbDevice getUsbDevice() { + return mUsbDevice; + } + + boolean isClosed() { + return mIsClosed; + } + + /** + * Register the own native pointer. + */ + @CalledByNative + void registerSelf(long nativePointer) { + mNativePointer = nativePointer; + } + + /** + * Sends a USB-MIDI data to the device. + * @param endpointNumber The endpoint number of the destination endpoint. + * @param bs The data to be sent. + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) + @CalledByNative + void send(int endpointNumber, byte[] bs) { + if (mIsClosed) { + return; + } + UsbEndpoint endpoint = mEndpointMap.get(endpointNumber); + if (endpoint == null) { + return; + } + if (shouldUseBulkTransfer()) { + // We use bulkTransfer instead of UsbRequest.queue because queueing + // a UsbRequest is currently not thread safe. + // Note that this is not exactly correct because we don't care + // about the transfer attribute (bmAttribute) of the endpoint. + // See also: + // http://stackoverflow.com/questions/9644415/ + // https://code.google.com/p/android/issues/detail?id=59467 + // + // TODO(yhirano): Delete this block once the problem is fixed. + final int timeout = 100; + mConnection.bulkTransfer(endpoint, bs, bs.length, timeout); + } else { + UsbRequest request = mRequestMap.get(endpoint); + if (request == null) { + request = new UsbRequest(); + request.initialize(mConnection, endpoint); + mRequestMap.put(endpoint, request); + } + request.queue(ByteBuffer.wrap(bs), bs.length); + } + } + + /** + * Returns true if |bulkTransfer| should be used in |send|. + * See comments in |send|. + */ + private boolean shouldUseBulkTransfer() { + return mHasInputThread; + } + + /** + * Returns the descriptors bytes of this device. + * @return The descriptors bytes of this device. + */ + @CalledByNative + byte[] getDescriptors() { + if (mConnection == null) { + return new byte[0]; + } + return mConnection.getRawDescriptors(); + } + + /** + * Returns the string descriptor bytes for the given index + * @param index index of the descriptor + * @return the string descriptor bytes for the given index. + */ + @CalledByNative + byte[] getStringDescriptor(int index) { + if (mConnection == null) { + return new byte[0]; + } + byte[] buffer = new byte[255]; + int type = UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD; + int request = REQUEST_GET_DESCRIPTOR; + int value = (STRING_DESCRIPTOR_TYPE << 8) | index; + int read = mConnection.controlTransfer(type, request, value, 0, buffer, buffer.length, 0); + if (read < 0) { + return new byte[0]; + } + return Arrays.copyOf(buffer, read); + } + + /** + * Closes the device connection. + */ + @CalledByNative + void close() { + mEndpointMap.clear(); + for (UsbRequest request : mRequestMap.values()) { + request.close(); + } + mRequestMap.clear(); + mConnection.close(); + mNativePointer = 0; + mIsClosed = true; + } + + /** + * Returns the length of a USB-MIDI input. + * Since the Android API doesn't provide us the length, + * we calculate it manually. + */ + private static int getInputDataLength(ByteBuffer buffer) { + int position = buffer.position(); + // We assume that the data length is always divisable by 4. + for (int i = 0; i < position; i += 4) { + // Since Code Index Number 0 is reserved, it is not a valid USB-MIDI data. + if (buffer.get(i) == 0) { + return i; + } + } + return position; + } + + private static native void nativeOnData( + long nativeUsbMidiDeviceAndroid, int endpointNumber, byte[] data); +} diff --git a/media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceFactoryAndroid.java b/media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceFactoryAndroid.java new file mode 100644 index 0000000..e003332 --- /dev/null +++ b/media/midi/java/src/org/chromium/media/midi/UsbMidiDeviceFactoryAndroid.java @@ -0,0 +1,264 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.media.midi; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; +import android.os.Parcelable; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Owned by its native counterpart declared in + * usb_midi_device_factory_android.h. Refer to that class for general comments. + */ +@JNINamespace("media::midi") +class UsbMidiDeviceFactoryAndroid { + /** + * The UsbManager of this system. + */ + private UsbManager mUsbManager; + + /** + * A BroadcastReceiver for USB device events. + */ + private BroadcastReceiver mReceiver; + + /** + * Accessible USB-MIDI devices got so far. + */ + private final List mDevices = new ArrayList(); + + /** + * Devices whose access permission requested but not resolved so far. + */ + private Set mRequestedDevices; + + /** + * True when the enumeration is in progress. + */ + private boolean mIsEnumeratingDevices; + + /** + * The identifier of this factory. + */ + private long mNativePointer; + + private static final String ACTION_USB_PERMISSION = "org.chromium.media.USB_PERMISSION"; + + /** + * Constructs a UsbMidiDeviceAndroid. + * @param context + * @param nativePointer The native pointer to which the created factory is associated. + */ + UsbMidiDeviceFactoryAndroid(Context context, long nativePointer) { + mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + mNativePointer = nativePointer; + mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Parcelable extra = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { + requestDevicePermissionIfNecessary(context, (UsbDevice) extra); + } + if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { + onUsbDeviceDetached((UsbDevice) extra); + } + if (ACTION_USB_PERMISSION.equals(intent.getAction())) { + onUsbDevicePermissionRequestDone(context, intent); + } + } + }; + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + filter.addAction(ACTION_USB_PERMISSION); + context.registerReceiver(mReceiver, filter); + mRequestedDevices = new HashSet(); + } + + /** + * Constructs a UsbMidiDeviceAndroid. + * @param context + * @param nativePointer The native pointer to which the created factory is associated. + */ + @CalledByNative + static UsbMidiDeviceFactoryAndroid create(Context context, long nativePointer) { + return new UsbMidiDeviceFactoryAndroid(context, nativePointer); + } + + /** + * Enumerates USB-MIDI devices. + * If there are devices having USB-MIDI interfaces, this function requests permission for + * accessing the device to the user. + * When the permission request is accepted or rejected, nativeOnUsbMidiDeviceRequestDone + * will be called. + * + * If there are no USB-MIDI interfaces, this function returns false. + * @param context + * @return true if some permission requests are in progress. + */ + @CalledByNative + boolean enumerateDevices(Context context) { + assert !mIsEnumeratingDevices; + mIsEnumeratingDevices = true; + Map devices = mUsbManager.getDeviceList(); + if (devices.isEmpty()) { + // No USB-MIDI devices are found. + mIsEnumeratingDevices = false; + return false; + } + for (UsbDevice device : devices.values()) { + requestDevicePermissionIfNecessary(context, device); + } + return true; + } + + /** + * Request a device access permission if there is a MIDI interface in the device. + * + * @param context + * @param device a USB device + */ + private void requestDevicePermissionIfNecessary(Context context, UsbDevice device) { + for (UsbDevice d : mRequestedDevices) { + if (d.getDeviceId() == device.getDeviceId()) { + // It is already requested. + return; + } + } + + for (int i = 0; i < device.getInterfaceCount(); ++i) { + UsbInterface iface = device.getInterface(i); + if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO + && iface.getInterfaceSubclass() == UsbMidiDeviceAndroid.MIDI_SUBCLASS) { + // There is at least one interface supporting MIDI. + mUsbManager.requestPermission( + device, PendingIntent.getBroadcast( + context, 0, new Intent(ACTION_USB_PERMISSION), 0)); + mRequestedDevices.add(device); + break; + } + } + } + + /** + * Called when a USB device is detached. + * + * @param device a USB device + */ + private void onUsbDeviceDetached(UsbDevice device) { + for (UsbDevice usbDevice : mRequestedDevices) { + if (usbDevice.getDeviceId() == device.getDeviceId()) { + mRequestedDevices.remove(usbDevice); + break; + } + } + for (int i = 0; i < mDevices.size(); ++i) { + UsbMidiDeviceAndroid midiDevice = mDevices.get(i); + if (midiDevice.isClosed()) { + // Once a device is disconnected, the system may reassign its device ID to + // another device. So we should ignore disconnected ones. + continue; + } + if (midiDevice.getUsbDevice().getDeviceId() == device.getDeviceId()) { + midiDevice.close(); + if (mIsEnumeratingDevices) { + // In this case, we don't have to keep mDevices sync with the devices list + // in MidiManagerUsb. + mDevices.remove(i); + return; + } + if (mNativePointer != 0) { + nativeOnUsbMidiDeviceDetached(mNativePointer, i); + } + return; + } + } + } + + /** + * Called when the user accepts or rejects the permission request requested by + * EnumerateDevices. + * + * @param context + * @param intent + */ + private void onUsbDevicePermissionRequestDone(Context context, Intent intent) { + UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + UsbMidiDeviceAndroid midiDevice = null; + if (mRequestedDevices.contains(device)) { + mRequestedDevices.remove(device); + if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + // The request was rejected. + device = null; + } + } else { + device = null; + } + + if (device != null) { + for (UsbMidiDeviceAndroid registered : mDevices) { + if (!registered.isClosed() + && registered.getUsbDevice().getDeviceId() == device.getDeviceId()) { + // The device is already registered. + device = null; + break; + } + } + } + + if (device != null) { + // Now we can add the device. + midiDevice = new UsbMidiDeviceAndroid(mUsbManager, device); + mDevices.add(midiDevice); + } + + if (!mRequestedDevices.isEmpty()) { + return; + } + if (mNativePointer == 0) { + return; + } + + if (mIsEnumeratingDevices) { + nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray()); + mIsEnumeratingDevices = false; + } else if (midiDevice != null) { + nativeOnUsbMidiDeviceAttached(mNativePointer, midiDevice); + } + } + + /** + * Disconnects the native object. + * @param context + */ + @CalledByNative + void close(Context context) { + mNativePointer = 0; + context.unregisterReceiver(mReceiver); + } + + private static native void nativeOnUsbMidiDeviceRequestDone( + long nativeUsbMidiDeviceFactoryAndroid, Object[] devices); + private static native void nativeOnUsbMidiDeviceAttached( + long nativeUsbMidiDeviceFactoryAndroid, Object device); + private static native void nativeOnUsbMidiDeviceDetached( + long nativeUsbMidiDeviceFactoryAndroid, int index); +} diff --git a/media/midi/midi.gyp b/media/midi/midi.gyp new file mode 100644 index 0000000..554f1a9 --- /dev/null +++ b/media/midi/midi.gyp @@ -0,0 +1,235 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + 'conditions': [ + ['(OS=="linux" or OS=="freebsd" or OS=="solaris") and (embedded!=1 or (chromecast==1 and target_arch!="arm"))', { + 'use_alsa%': 1, + }, { + 'use_alsa%': 0, + }], + ], + # Source files that are used in production code for Android, and in tests + # for others. + 'usb_midi_sources': [ + 'midi_manager_usb.cc', + 'midi_manager_usb.h', + 'usb_midi_descriptor_parser.cc', + 'usb_midi_descriptor_parser.h', + 'usb_midi_device.h', + 'usb_midi_input_stream.cc', + 'usb_midi_input_stream.h', + 'usb_midi_jack.h', + 'usb_midi_output_stream.cc', + 'usb_midi_output_stream.h', + ], + }, + 'targets': [ + { + # GN version: //media/midi + 'target_name': 'midi', + 'type': '<(component)', + 'dependencies': [ + '../../base/base.gyp:base', + ], + 'defines': [ + 'MEDIA_IMPLEMENTATION', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'midi_manager.cc', + 'midi_manager.h', + 'midi_manager_android.cc', + 'midi_manager_mac.cc', + 'midi_manager_mac.h', + 'midi_manager_win.cc', + 'midi_manager_win.h', + 'midi_message_queue.cc', + 'midi_message_queue.h', + 'midi_message_util.cc', + 'midi_message_util.h', + 'midi_port_info.cc', + 'midi_port_info.h', + 'midi_scheduler.cc', + 'midi_scheduler.h', + 'usb_midi_device_android.cc', + 'usb_midi_device_android.h', + 'usb_midi_device_factory_android.cc', + 'usb_midi_device_factory_android.h', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../..', + ], + }, + 'conditions': [ + ['use_alsa==1 and use_udev==1', { + 'dependencies': [ + '../../crypto/crypto.gyp:crypto', + '../../device/udev_linux/udev.gyp:udev_linux', + ], + 'defines': [ + 'USE_ALSA', + 'USE_UDEV', + ], + 'sources': [ + 'midi_manager_alsa.cc', + 'midi_manager_alsa.h', + ], + 'link_settings': { + 'libraries': [ + '-lasound', + ], + }, + }], + ['OS=="android"', { + 'dependencies': [ + '../../base/base.gyp:base_i18n', + 'midi_jni_headers', + 'midi_java', + ], + 'sources': [ + '<@(usb_midi_sources)', + 'midi_jni_registrar.cc', + 'midi_jni_registrar.h', + ], + }], + ['OS=="mac"', { + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/CoreAudio.framework', + '$(SDKROOT)/System/Library/Frameworks/CoreMIDI.framework', + ], + }, + }], + ['OS=="win"', { + 'dependencies': [ + '../../device/usb/usb.gyp:device_usb', + ], + }], + ], # conditions + }, + { + # GN version: //media/midi:midi_unittests + 'target_name': 'midi_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'midi', + '../../base/base.gyp:base', + '../../base/base.gyp:run_all_unittests', + '../../testing/gtest.gyp:gtest', + ], + 'defines': [ + 'MEDIA_IMPLEMENTATION', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'midi_manager_unittest.cc', + 'midi_manager_usb_unittest.cc', + 'midi_message_queue_unittest.cc', + 'midi_message_util_unittest.cc', + 'usb_midi_descriptor_parser_unittest.cc', + 'usb_midi_input_stream_unittest.cc', + 'usb_midi_output_stream_unittest.cc', + ], + 'conditions': [ + ['use_alsa==1 and use_udev==1', { + 'defines': [ + 'USE_ALSA', + 'USE_UDEV', + ], + 'sources': [ + 'midi_manager_alsa_unittest.cc', + ], + }], + ['use_x11==1', { + 'dependencies': [ + '../../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck', + ], + }], + ['OS=="android"', { + 'dependencies': [ + '../../testing/android/native_test.gyp:native_test_native_code', + ], + }, { + 'sources': [ + '<@(usb_midi_sources)', + ], + }], + ['OS=="mac"', { + 'sources': [ + 'midi_manager_mac_unittest.cc', + ], + }], + ], + }, + ], + 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + # TODO(GN) + 'target_name': 'midi_unittests_apk', + 'type': 'none', + 'dependencies': [ + 'midi_java', + 'midi_unittests', + ], + 'variables': { + 'test_suite_name': 'midi_unittests', + }, + 'includes': ['../../build/apk_test.gypi'], + }, + { + # GN: //media/midi:midi_java + 'target_name': 'midi_java', + 'type': 'none', + 'dependencies': [ + '../../base/base.gyp:base', + ], + 'variables': { + 'java_in_dir': 'java', + }, + 'includes': ['../../build/java.gypi' ], + }, + { + # GN: //media/midi:midi_jni_headers + 'target_name': 'midi_jni_headers', + 'type': 'none', + 'sources': [ + 'java/src/org/chromium/media/midi/UsbMidiDeviceAndroid.java', + 'java/src/org/chromium/media/midi/UsbMidiDeviceFactoryAndroid.java', + ], + 'variables': { + 'jni_gen_package': 'media/midi', + }, + 'includes': ['../../build/jni_generator.gypi'], + }, + ], + }], + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'midi_unittests_run', + 'type': 'none', + 'dependencies': [ + 'midi_unittests', + ], + 'includes': [ + '../../build/isolate.gypi', + ], + 'sources': [ + 'midi_unittests.isolate', + ], + }, + ], + }], + ], +} diff --git a/media/midi/midi_jni_registrar.cc b/media/midi/midi_jni_registrar.cc new file mode 100644 index 0000000..a0028cd --- /dev/null +++ b/media/midi/midi_jni_registrar.cc @@ -0,0 +1,29 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/midi/midi_jni_registrar.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "base/basictypes.h" + +#include "media/midi/usb_midi_device_android.h" +#include "media/midi/usb_midi_device_factory_android.h" + +namespace media { +namespace midi { + +static base::android::RegistrationMethod kMediaRegisteredMethods[] = { + {"UsbMidiDevice", UsbMidiDeviceAndroid::RegisterUsbMidiDevice}, + {"UsbMidiDeviceFactory", + UsbMidiDeviceFactoryAndroid::RegisterUsbMidiDeviceFactory}, +}; + +bool RegisterJni(JNIEnv* env) { + return base::android::RegisterNativeMethods( + env, kMediaRegisteredMethods, arraysize(kMediaRegisteredMethods)); +} + +} // namespace midi +} // namespace media diff --git a/media/midi/midi_jni_registrar.h b/media/midi/midi_jni_registrar.h new file mode 100644 index 0000000..f981e5c --- /dev/null +++ b/media/midi/midi_jni_registrar.h @@ -0,0 +1,21 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_MIDI_MIDI_JNI_REGISTRAR_H_ +#define MEDIA_MIDI_MIDI_JNI_REGISTRAR_H_ + +#include + +#include "media/base/media_export.h" + +namespace media { +namespace midi { + +// Register all JNI bindings necessary for media/midi. +MEDIA_EXPORT bool RegisterJni(JNIEnv* env); + +} // namespace midi +} // namespace media + +#endif // MEDIA_MIDI_MIDI_JNI_REGISTRAR_H_ diff --git a/media/midi/midi_manager_usb.cc b/media/midi/midi_manager_usb.cc index 346098c..4548a89 100644 --- a/media/midi/midi_manager_usb.cc +++ b/media/midi/midi_manager_usb.cc @@ -74,7 +74,7 @@ void MidiManagerUsb::ReceiveUsbMidiData(UsbMidiDevice* device, } void MidiManagerUsb::OnDeviceAttached(scoped_ptr device) { - int device_id = devices_.size(); + int device_id = static_cast(devices_.size()); devices_.push_back(device.release()); AddPorts(devices_.back(), device_id); } @@ -86,13 +86,13 @@ void MidiManagerUsb::OnDeviceDetached(size_t index) { UsbMidiDevice* device = devices_[index]; for (size_t i = 0; i < output_streams_.size(); ++i) { if (output_streams_[i]->jack().device == device) { - SetOutputPortState(i, MIDI_PORT_DISCONNECTED); + SetOutputPortState(static_cast(i), MIDI_PORT_DISCONNECTED); } } const std::vector& input_jacks = input_stream_->jacks(); for (size_t i = 0; i < input_jacks.size(); ++i) { if (input_jacks[i].device == device) { - SetInputPortState(i, MIDI_PORT_DISCONNECTED); + SetInputPortState(static_cast(i), MIDI_PORT_DISCONNECTED); } } } @@ -101,7 +101,7 @@ void MidiManagerUsb::OnReceivedData(size_t jack_index, const uint8* data, size_t size, base::TimeTicks time) { - ReceiveMidiData(jack_index, data, size, time); + ReceiveMidiData(static_cast(jack_index), data, size, time); } @@ -114,7 +114,7 @@ void MidiManagerUsb::OnEnumerateDevicesDone(bool result, input_stream_.reset(new UsbMidiInputStream(this)); devices->swap(devices_); for (size_t i = 0; i < devices_.size(); ++i) { - if (!AddPorts(devices_[i], i)) { + if (!AddPorts(devices_[i], static_cast(i))) { initialize_callback_.Run(MIDI_INITIALIZATION_ERROR); return; } diff --git a/media/midi/midi_manager_win.cc b/media/midi/midi_manager_win.cc index 5937278..eb729ea 100644 --- a/media/midi/midi_manager_win.cc +++ b/media/midi/midi_manager_win.cc @@ -101,7 +101,7 @@ ScopedMIDIHDR CreateMIDIHDR(size_t size) { ScopedMIDIHDR header(new MIDIHDR); ZeroMemory(header.get(), sizeof(*header)); header->lpData = new char[size]; - header->dwBufferLength = size; + header->dwBufferLength = static_cast(size); return header.Pass(); } @@ -619,7 +619,7 @@ class MidiServiceWinImpl : public MidiServiceWin, base::AutoLock auto_lock(input_ports_lock_); const auto it = unused_input_ports_.find(state_device_info); if (it == unused_input_ports_.end()) { - port_number = input_ports_.size(); + port_number = static_cast(input_ports_.size()); add_new_port = true; input_ports_.push_back(nullptr); input_ports_ages_.push_back(0); @@ -800,7 +800,7 @@ class MidiServiceWinImpl : public MidiServiceWin, base::AutoLock auto_lock(output_ports_lock_); const auto it = unused_output_ports_.find(state_device_info); if (it == unused_output_ports_.end()) { - port_number = output_ports_.size(); + port_number = static_cast(output_ports_.size()); add_new_port = true; output_ports_.push_back(nullptr); output_ports_ages_.push_back(0); diff --git a/media/midi/midi_scheduler.h b/media/midi/midi_scheduler.h index 73c8bc6..452328b 100644 --- a/media/midi/midi_scheduler.h +++ b/media/midi/midi_scheduler.h @@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" +#include "media/base/media_export.h" namespace media { @@ -14,7 +15,7 @@ class MidiManager; class MidiManagerClient; // TODO(crbug.com/467442): Make tasks cancelable per client. -class MidiScheduler final { +class MEDIA_EXPORT MidiScheduler final { public: explicit MidiScheduler(MidiManager* manager); ~MidiScheduler(); diff --git a/media/midi/midi_unittests.isolate b/media/midi/midi_unittests.isolate new file mode 100644 index 0000000..dca0d35 --- /dev/null +++ b/media/midi/midi_unittests.isolate @@ -0,0 +1,63 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'includes': [ + '../../base/base.isolate', + ], + 'conditions': [ + ['use_x11==0', { + 'variables': { + 'command': [ + '../../testing/test_env.py', + '<(PRODUCT_DIR)/midi_unittests<(EXECUTABLE_SUFFIX)', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + '--asan=<(asan)', + '--msan=<(msan)', + '--tsan=<(tsan)', + ], + }, + }], + ['use_x11==1', { + 'variables': { + 'command': [ + '../../testing/xvfb.py', + '<(PRODUCT_DIR)', + '<(PRODUCT_DIR)/midi_unittests', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + '--asan=<(asan)', + '--msan=<(msan)', + '--tsan=<(tsan)', + ], + 'files': [ + '../../testing/xvfb.py', + '<(PRODUCT_DIR)/xdisplaycheck', + ], + }, + }], + ['OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'files': [ + '../../testing/test_env.py', + '<(PRODUCT_DIR)/midi_unittests<(EXECUTABLE_SUFFIX)', + ], + }, + }], + ['OS=="mac" and asan==1 and fastbuild==0', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/midi_unittests.dSYM/', + ], + }, + }], + ['OS=="win" and (fastbuild==0 or fastbuild==1)', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/midi_unittests.exe.pdb', + ], + }, + }], + ], +} diff --git a/media/midi/usb_midi_device_android.cc b/media/midi/usb_midi_device_android.cc index f62eb86..640ef22 100644 --- a/media/midi/usb_midi_device_android.cc +++ b/media/midi/usb_midi_device_android.cc @@ -20,8 +20,8 @@ UsbMidiDeviceAndroid::UsbMidiDeviceAndroid(ObjectRef raw_device, UsbMidiDeviceDelegate* delegate) : raw_device_(raw_device), delegate_(delegate) { JNIEnv* env = base::android::AttachCurrentThread(); - Java_UsbMidiDeviceAndroid_registerSelf( - env, raw_device_.obj(), reinterpret_cast(this)); + midi::Java_UsbMidiDeviceAndroid_registerSelf(env, raw_device_.obj(), + reinterpret_cast(this)); GetDescriptorsInternal(); InitDeviceInfo(); @@ -29,7 +29,7 @@ UsbMidiDeviceAndroid::UsbMidiDeviceAndroid(ObjectRef raw_device, UsbMidiDeviceAndroid::~UsbMidiDeviceAndroid() { JNIEnv* env = base::android::AttachCurrentThread(); - Java_UsbMidiDeviceAndroid_close(env, raw_device_.obj()); + midi::Java_UsbMidiDeviceAndroid_close(env, raw_device_.obj()); } std::vector UsbMidiDeviceAndroid::GetDescriptors() { @@ -55,8 +55,8 @@ void UsbMidiDeviceAndroid::Send(int endpoint_number, ScopedJavaLocalRef data_to_pass = base::android::ToJavaByteArray(env, head, data.size()); - Java_UsbMidiDeviceAndroid_send( - env, raw_device_.obj(), endpoint_number, data_to_pass.obj()); + midi::Java_UsbMidiDeviceAndroid_send(env, raw_device_.obj(), endpoint_number, + data_to_pass.obj()); } void UsbMidiDeviceAndroid::OnData(JNIEnv* env, @@ -72,13 +72,13 @@ void UsbMidiDeviceAndroid::OnData(JNIEnv* env, } bool UsbMidiDeviceAndroid::RegisterUsbMidiDevice(JNIEnv* env) { - return RegisterNativesImpl(env); + return midi::RegisterNativesImpl(env); } void UsbMidiDeviceAndroid::GetDescriptorsInternal() { JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef descriptors = - Java_UsbMidiDeviceAndroid_getDescriptors(env, raw_device_.obj()); + midi::Java_UsbMidiDeviceAndroid_getDescriptors(env, raw_device_.obj()); base::android::JavaByteArrayToByteVector(env, descriptors.obj(), &descriptors_); @@ -111,8 +111,8 @@ void UsbMidiDeviceAndroid::InitDeviceInfo() { std::vector UsbMidiDeviceAndroid::GetStringDescriptor(int index) { JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef descriptors = - Java_UsbMidiDeviceAndroid_getStringDescriptor(env, raw_device_.obj(), - index); + midi::Java_UsbMidiDeviceAndroid_getStringDescriptor( + env, raw_device_.obj(), index); std::vector ret; base::android::JavaByteArrayToByteVector(env, descriptors.obj(), &ret); diff --git a/media/midi/usb_midi_device_factory_android.cc b/media/midi/usb_midi_device_factory_android.cc index 6302572..f066428 100644 --- a/media/midi/usb_midi_device_factory_android.cc +++ b/media/midi/usb_midi_device_factory_android.cc @@ -30,7 +30,7 @@ UsbMidiDeviceFactoryAndroid::UsbMidiDeviceFactoryAndroid() : delegate_(NULL) {} UsbMidiDeviceFactoryAndroid::~UsbMidiDeviceFactoryAndroid() { JNIEnv* env = base::android::AttachCurrentThread(); if (!raw_factory_.is_null()) - Java_UsbMidiDeviceFactoryAndroid_close( + midi::Java_UsbMidiDeviceFactoryAndroid_close( env, raw_factory_.obj(), base::android::GetApplicationContext()); } @@ -40,13 +40,13 @@ void UsbMidiDeviceFactoryAndroid::EnumerateDevices( DCHECK(!delegate_); JNIEnv* env = base::android::AttachCurrentThread(); uintptr_t pointer = reinterpret_cast(this); - raw_factory_.Reset(Java_UsbMidiDeviceFactoryAndroid_create( + raw_factory_.Reset(midi::Java_UsbMidiDeviceFactoryAndroid_create( env, base::android::GetApplicationContext(), pointer)); delegate_ = delegate; callback_ = callback; - if (Java_UsbMidiDeviceFactoryAndroid_enumerateDevices( + if (midi::Java_UsbMidiDeviceFactoryAndroid_enumerateDevices( env, raw_factory_.obj(), base::android::GetApplicationContext())) { // Asynchronous operation. return; @@ -92,7 +92,7 @@ void UsbMidiDeviceFactoryAndroid::OnUsbMidiDeviceDetached( } bool UsbMidiDeviceFactoryAndroid::RegisterUsbMidiDeviceFactory(JNIEnv* env) { - return RegisterNativesImpl(env); + return midi::RegisterNativesImpl(env); } } // namespace media diff --git a/media/midi/usb_midi_output_stream.cc b/media/midi/usb_midi_output_stream.cc index 2f32897..8f1af70 100644 --- a/media/midi/usb_midi_output_stream.cc +++ b/media/midi/usb_midi_output_stream.cc @@ -98,7 +98,7 @@ bool UsbMidiOutputStream::PushSysExMessage(const std::vector& data, message[message_size] = byte; ++message_size; if (byte == kEndOfSysExByte) { - uint8 code_index = message_size + 0x4; + uint8 code_index = static_cast(message_size) + 0x4; DCHECK(code_index == 0x5 || code_index == 0x6 || code_index == 0x7); data_to_send->push_back((jack_.cable_number << 4) | code_index); data_to_send->insert(data_to_send->end(), -- cgit v1.1