diff options
25 files changed, 633 insertions, 1431 deletions
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java index a99573692664..6096355ddb05 100644 --- a/media/java/android/media/midi/MidiDevice.java +++ b/media/java/android/media/midi/MidiDevice.java @@ -37,30 +37,17 @@ import java.util.HashSet; * Instances of this class are created by {@link MidiManager#openDevice}. */ public final class MidiDevice implements Closeable { - static { - System.loadLibrary("media_jni"); - } - private static final String TAG = "MidiDevice"; - private final MidiDeviceInfo mDeviceInfo; + private final MidiDeviceInfo mDeviceInfo; // accessed from native code private final IMidiDeviceServer mDeviceServer; + private final IBinder mDeviceServerBinder; // accessed from native code private final IMidiManager mMidiManager; private final IBinder mClientToken; private final IBinder mDeviceToken; - private boolean mIsDeviceClosed; + private boolean mIsDeviceClosed; // accessed from native code - // Native API Helpers - /** - * Keep a static list of MidiDevice objects that are mirrorToNative()'d so they - * don't get inadvertantly garbage collected. - */ - private static HashSet<MidiDevice> mMirroredDevices = new HashSet<MidiDevice>(); - - /** - * If this device is mirrorToNatived(), this is the native device handler. - */ - private long mNativeHandle; + private long mNativeHandle; // accessed from native code private final CloseGuard mGuard = CloseGuard.get(); @@ -118,6 +105,7 @@ public final class MidiDevice implements Closeable { IMidiManager midiManager, IBinder clientToken, IBinder deviceToken) { mDeviceInfo = deviceInfo; mDeviceServer = server; + mDeviceServerBinder = mDeviceServer.asBinder(); mMidiManager = midiManager; mClientToken = clientToken; mDeviceToken = deviceToken; @@ -230,50 +218,15 @@ public final class MidiDevice implements Closeable { } } - /** - * Makes Midi Device available to the Native API - * @hide - */ - public long mirrorToNative() throws IOException { - if (mIsDeviceClosed || mNativeHandle != 0) { - return 0; - } - - mNativeHandle = native_mirrorToNative(mDeviceServer.asBinder(), mDeviceInfo.getId()); - if (mNativeHandle == 0) { - throw new IOException("Failed mirroring to native"); - } - - synchronized (mMirroredDevices) { - mMirroredDevices.add(this); - } - return mNativeHandle; - } - - /** - * Makes Midi Device no longer available to the Native API - * @hide - */ - public void removeFromNative() { - if (mNativeHandle == 0) { - return; - } - - synchronized (mGuard) { - native_removeFromNative(mNativeHandle); - mNativeHandle = 0; - } - - synchronized (mMirroredDevices) { - mMirroredDevices.remove(this); - } - } - @Override public void close() throws IOException { synchronized (mGuard) { - if (!mIsDeviceClosed) { - removeFromNative(); + // What if there is a native reference to this? + if (mNativeHandle != 0) { + Log.w(TAG, "MidiDevice#close() called while there is an outstanding native client 0x" + + Long.toHexString(mNativeHandle)); + } + if (!mIsDeviceClosed && mNativeHandle == 0) { mGuard.close(); mIsDeviceClosed = true; try { @@ -302,7 +255,4 @@ public final class MidiDevice implements Closeable { public String toString() { return ("MidiDevice: " + mDeviceInfo.toString()); } - - private native long native_mirrorToNative(IBinder deviceServerBinder, int id); - private native void native_removeFromNative(long deviceHandle); } diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java index 5fd9006db97c..ab8af2a87092 100644 --- a/media/java/android/media/midi/MidiDeviceInfo.java +++ b/media/java/android/media/midi/MidiDeviceInfo.java @@ -190,7 +190,7 @@ public final class MidiDeviceInfo implements Parcelable { } private final int mType; // USB or virtual - private final int mId; // unique ID generated by MidiService + private final int mId; // unique ID generated by MidiService. Accessed from native code. private final int mInputPortCount; private final int mOutputPortCount; private final String[] mInputPortNames; diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 44e5d61361cd..272b0b7c442a 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -25,7 +25,6 @@ cc_library_shared { "android_mtp_MtpDatabase.cpp", "android_mtp_MtpDevice.cpp", "android_mtp_MtpServer.cpp", - "midi/android_media_midi_MidiDevice.cpp", ], shared_libs: [ @@ -39,7 +38,6 @@ cc_library_shared { "libmedia_omx", "libmediametrics", "libmediadrm", - "libmidi", "libhwui", "libui", "liblog", diff --git a/media/jni/midi/android_media_midi_MidiDevice.cpp b/media/jni/midi/android_media_midi_MidiDevice.cpp deleted file mode 100644 index 5b35453c32d0..000000000000 --- a/media/jni/midi/android_media_midi_MidiDevice.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "Midi-JNI" - -#include <android_util_Binder.h> -#include <jni.h> -#include <midi_internal.h> -#include <utils/Log.h> - -using namespace android; -using namespace android::media::midi; - -extern "C" jlong Java_android_media_midi_MidiDevice_native_1mirrorToNative( - JNIEnv *env, jobject, jobject midiDeviceServer, jint id) -{ - // ALOGI("native_mirrorToNative(%p)...", midiDeviceServer); - sp<IBinder> serverBinder = ibinderForJavaObject(env, midiDeviceServer); - if (serverBinder.get() == NULL) { - ALOGE("Could not obtain IBinder from passed jobject"); - return -EINVAL; - } - - AMIDI_Device* devicePtr = new AMIDI_Device; - devicePtr->server = new BpMidiDeviceServer(serverBinder); - devicePtr->deviceId = id; - - return (jlong)devicePtr; -} - -extern "C" void Java_android_media_midi_MidiDevice_native_removeFromNative( - JNIEnv *, jobject , jlong nativeToken) -{ - AMIDI_Device* devicePtr = (AMIDI_Device*)nativeToken; - delete devicePtr; -} diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp index 11f0deabbcd5..d069bd2f6284 100644 --- a/media/native/midi/Android.bp +++ b/media/native/midi/Android.bp @@ -13,7 +13,7 @@ // limitations under the License. cc_library_shared { - name: "libmidi", + name: "libamidi", srcs: [ "midi.cpp", @@ -22,13 +22,13 @@ cc_library_shared { aidl: { include_dirs: ["frameworks/base/media/java"], - export_aidl_headers: true, + export_aidl_headers: false, }, cflags: [ "-Wall", "-Werror", - "-O0", + "-fvisibility=hidden", ], shared_libs: [ @@ -36,7 +36,28 @@ cc_library_shared { "libbinder", "libutils", "libmedia", + "libmediandk", + "libandroid_runtime", ], - export_include_dirs: ["."], + export_include_dirs: ["include"], +} + +ndk_headers { + name: "amidi", + + from: "include", + + to: "amidi", + + srcs: ["include/midi.h"], + license: "include/NOTICE", +} + +ndk_library { + name: "libamidi", + + symbol_file: "libamidi.map.txt", + + first_version: "29", } diff --git a/media/native/midi/include/NOTICE b/media/native/midi/include/NOTICE new file mode 100644 index 000000000000..e72ff94a8401 --- /dev/null +++ b/media/native/midi/include/NOTICE @@ -0,0 +1,13 @@ +Copyright (C) 2018 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. diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/midi.h new file mode 100644 index 000000000000..780d8a7bfd40 --- /dev/null +++ b/media/native/midi/include/midi.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef ANDROID_MEDIA_MIDI_H_ +#define ANDROID_MEDIA_MIDI_H_ + +#include <stdarg.h> +#include <stdint.h> +#include <sys/types.h> + +#include <jni.h> + +#include <media/NdkMediaError.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AMidiDevice AMidiDevice; +typedef struct AMidiInputPort AMidiInputPort; +typedef struct AMidiOutputPort AMidiOutputPort; + +#define AMIDI_API __attribute__((visibility("default"))) + +/* + * Message Op Codes. Used to parse MIDI data packets + */ +enum { + AMIDI_OPCODE_DATA = 1, /* The MIDI packet contains normal MIDI data */ + AMIDI_OPCODE_FLUSH = 2, /* The MIDI packet contains just a MIDI FLUSH command. */ + /* Forces the send of any pending MIDI data. */ +}; + +/* + * Type IDs for various MIDI devices. + */ +enum { + AMIDI_DEVICE_TYPE_USB = 1, /* A MIDI device connected to the Android USB port */ + AMIDI_DEVICE_TYPE_VIRTUAL = 2, /* A software object implementing MidiDeviceService */ + AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */ +}; + +/* + * Device API + */ +/** + * Connects a native Midi Device object to the associated Java MidiDevice object. Use this + * AMidiDevice to access the rest of the native MIDI API. Use AMidiDevice_release() to + * disconnect from the Java object when not being used any more. + * + * @param env Points to the Java Environment. + * @param midiDeviceObj The Java MidiDevice Object. + * @param outDevicePtrPtr Points to the pointer to receive the AMidiDevice + * + * @return AMEDIA_OK on success, or a negative error value: + * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} - the midiDeviceObj + * is null or already connected to a native AMidiDevice + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred. + */ +media_status_t AMIDI_API AMidiDevice_fromJava( + JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr); + +/** + * Disconnects the native Midi Device Object from the associated Java MidiDevice object. + * + * @param midiDevice Points to the native AMIDI_MidiDevice. + * + * @return AMEDIA_OK on success, + * or a negative error value: + * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} + * - the device parameter is NULL. + * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} + * - the device is not consistent with the associated Java MidiDevice. + * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} + * - the JNI interface initialization to the associated java MidiDevice failed. + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info. + */ +media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice); + +/** + * Gets the MIDI device type. + * + * @param device Specifies the MIDI device. + * + * @return The identifier of the MIDI device type: + * AMIDI_DEVICE_TYPE_USB + * AMIDI_DEVICE_TYPE_VIRTUAL + * AMIDI_DEVICE_TYPE_BLUETOOTH + * or a negative error value: + * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device + * parameter is NULL. + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error. + */ +int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device); + +/** + * Gets the number of input (sending) ports available on the specified MIDI device. + * + * @param device Specifies the MIDI device. + * + * @return If successful, returns the number of MIDI input (sending) ports available on the + * device. If an error occurs, returns a negative value indicating the error: + * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device + * parameter is NULL. + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info. + */ +ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device); + +/** + * Gets the number of output (receiving) ports available on the specified MIDI device. + * + * @param device Specifies the MIDI device. + * + * @return If successful, returns the number of MIDI output (receiving) ports available on the + * device. If an error occurs, returns a negative value indicating the error: + * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device + * parameter is NULL. + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info. + */ +ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device); + +/* + * API for receiving data from the Output port of a device. + */ +/** + * Opens the output port so that the client can receive data from it. The port remains open and + * valid until AMidiOutputPort_close() is called for the returned AMidiOutputPort. + * + * @param device Specifies the MIDI device. + * @param portNumber Specifies the zero-based port index on the device to open. This value ranges + * between 0 and one less than the number of output ports reported by the + * AMidiDevice_getNumOutputPorts function. + * @param outOutputPortPtr Receives the native API port identifier of the opened port. + * + * @return AMEDIA_OK, or a negative error code: + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error. + */ +media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber, + AMidiOutputPort **outOutputPortPtr); + +/** + * Closes the output port. + * + * @param outputPort The native API port identifier of the port. + */ +void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort); + +/** + * Receives the next pending MIDI message. To retrieve all pending messages, the client should + * repeatedly call this method until it returns 0. + * + * Note that this is a non-blocking call. If there are no Midi messages are available, the function + * returns 0 immediately (for 0 messages received). + * + * @param outputPort Identifies the port to receive messages from. + * @param opcodePtr Receives the message Op Code. + * @param buffer Points to the buffer to receive the message data bytes. + * @param maxBytes Specifies the size of the buffer pointed to by the buffer parameter. + * @param numBytesReceivedPtr On exit, receives the actual number of bytes stored in buffer. + * @param outTimestampPtr If non-NULL, receives the timestamp associated with the message. + * (the current value of the running Java Virtual Machine's high-resolution time source, + * in nanoseconds) + * @return the number of messages received (either 0 or 1), or a negative error code: + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error. + */ +ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr, + uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr); + +/* + * API for sending data to the Input port of a device. + */ +/** + * Opens the input port so that the client can send data to it. The port remains open and + * valid until AMidiInputPort_close() is called for the returned AMidiInputPort. + * + * @param device Specifies the MIDI device. + * @param portNumber Specifies the zero-based port index on the device to open. This value ranges + * between 0 and one less than the number of input ports reported by the + * AMidiDevice_getNumInputPorts() function.. + * @param outInputPortPtr Receives the native API port identifier of the opened port. + * + * @return AMEDIA_OK, or a negative error code: + * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error. + */ +media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber, + AMidiInputPort **outInputPortPtr); + +/** + * Sends data to the specified input port. + * + * @param inputPort The identifier of the port to send data to. + * @param buffer Points to the array of bytes containing the data to send. + * @param numBytes Specifies the number of bytes to write. + * + * @return The number of bytes sent, which could be less than specified or a negative error code: + * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port + * was NULL, the specified buffer was NULL. + */ +ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer, + size_t numBytes); + +/** + * Sends data to the specified input port with a timestamp. + * + * @param inputPort The identifier of the port to send data to. + * @param buffer Points to the array of bytes containing the data to send. + * @param numBytes Specifies the number of bytes to write. + * @param timestamp The CLOCK_MONOTONIC time in nanoseconds to associate with the sent data. + * + * @return The number of bytes sent, which could be less than specified or a negative error code: + * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port + * was NULL, the specified buffer was NULL. + */ +ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, + const uint8_t *buffer, size_t numBytes, int64_t timestamp); + +/** + * Sends a message with a 'MIDI flush command code' to the specified port. This should cause + * a receiver to discard any pending MIDI data it may have accumulated and not processed. + * + * @param inputPort The identifier of the port to send the flush command to. + * + * @returns @see AMEDIA_OK {@link AMEDIA_OK} if successful, otherwise a negative error code: + * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port + * was NULL + * @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't + * be sent. + */ +media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort); + +/** + * Closes the input port. + * + * @param inputPort Identifies the input (sending) port to close. + */ +void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort); + +#ifdef __cplusplus +} +#endif + +#endif /* ANDROID_MEDIA_MIDI_H_ */ diff --git a/media/native/midi/libamidi.map.txt b/media/native/midi/libamidi.map.txt new file mode 100644 index 000000000000..62627f8c5ef7 --- /dev/null +++ b/media/native/midi/libamidi.map.txt @@ -0,0 +1,18 @@ +LIBAMIDI { + global: + AMidiDevice_fromJava; + AMidiDevice_release; + AMidiDevice_getType; + AMidiDevice_getNumInputPorts; + AMidiDevice_getNumOutputPorts; + AMidiOutputPort_open; + AMidiOutputPort_close; + AMidiOutputPort_receive; + AMidiInputPort_open; + AMidiInputPort_send; + AMidiInputPort_sendWithTimestamp; + AMidiInputPort_sendFlush; + AMidiInputPort_close; + local: + *; +}; diff --git a/media/native/midi/midi.cpp b/media/native/midi/midi.cpp index 17b92da3325e..a9b498ffe0c8 100644 --- a/media/native/midi/midi.cpp +++ b/media/native/midi/midi.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 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. @@ -20,15 +20,19 @@ #include <unistd.h> #include <binder/Binder.h> -#include <utils/Errors.h> +#include <android_util_Binder.h> #include <utils/Log.h> +#include <core_jni_helpers.h> + #include "android/media/midi/BpMidiDeviceServer.h" #include "media/MidiDeviceInfo.h" -#include "midi.h" +#include "include/midi.h" #include "midi_internal.h" +using namespace android::media::midi; + using android::IBinder; using android::BBinder; using android::OK; @@ -36,23 +40,26 @@ using android::sp; using android::status_t; using android::base::unique_fd; using android::binder::Status; -using android::media::midi::MidiDeviceInfo; struct AMIDI_Port { - std::atomic_int state; - AMIDI_Device *device; - sp<IBinder> binderToken; - unique_fd ufd; + std::atomic_int state; // One of the port status constants below. + const AMidiDevice *device; // Points to the AMidiDevice associated with the port. + sp<IBinder> binderToken;// The Binder token associated with the port. + unique_fd ufd; // The unique file descriptor associated with the port. }; -#define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE - +/* + * Port Status Constants + */ enum { MIDI_PORT_STATE_CLOSED = 0, MIDI_PORT_STATE_OPEN_IDLE, MIDI_PORT_STATE_OPEN_ACTIVE }; +/* + * Port Type Constants + */ enum { PORTTYPE_OUTPUT = 0, PORTTYPE_INPUT = 1 @@ -76,32 +83,192 @@ enum { * boundaries, and delivers messages in the order that they were sent. * So 'read()' always returns a whole message. */ +#define AMIDI_PACKET_SIZE 1024 +#define AMIDI_PACKET_OVERHEAD 9 +#define AMIDI_BUFFER_SIZE (AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD) + +// MidiDevice Fields +static jobject deviceClassGlobalRef = nullptr; // A GlobalRef for MidiDevice Class +static jfieldID fidDeviceClosed = nullptr; // MidiDevice.mIsDeviceClosed +static jfieldID fidNativeHandle = nullptr; // MidiDevice.mNativeHandle +static jfieldID fidDeviceServerBinder = nullptr; // MidiDevice.mDeviceServerBinder +static jfieldID fidDeviceInfo = nullptr; // MidiDevice.mDeviceInfo + +// MidiDeviceInfo Fields +static jobject deviceInfoClassGlobalRef = nullptr; // A GlobalRef for MidiDeviceInfoClass +static jfieldID fidDeviceId = nullptr; // MidiDeviceInfo.mId + +static std::mutex openMutex; // Ensure that the device can be connected just once to 1 thread + +static void AMIDI_initJNI(JNIEnv *env) { + jclass deviceClass = android::FindClassOrDie(env, "android/media/midi/MidiDevice"); + deviceClassGlobalRef = env->NewGlobalRef(deviceClass); + + // MidiDevice Field IDs + fidDeviceClosed = android::GetFieldIDOrDie(env, deviceClass, "mIsDeviceClosed", "Z"); + fidNativeHandle = android::GetFieldIDOrDie(env, deviceClass, "mNativeHandle", "J"); + fidDeviceServerBinder = android::GetFieldIDOrDie(env, deviceClass, + "mDeviceServerBinder", "Landroid/os/IBinder;"); + fidDeviceInfo = android::GetFieldIDOrDie(env, deviceClass, + "mDeviceInfo", "Landroid/media/midi/MidiDeviceInfo;"); + + // MidiDeviceInfo Field IDs + jclass deviceInfoClass = android::FindClassOrDie(env, "android/media/midi/MidiDeviceInfo"); + deviceInfoClassGlobalRef = env->NewGlobalRef(deviceInfoClass); + fidDeviceId = android::GetFieldIDOrDie(env, deviceInfoClass, "mId", "I"); +} + +//// Handy debugging function. +//static void AMIDI_logBuffer(const uint8_t *data, size_t numBytes) { +// for (size_t index = 0; index < numBytes; index++) { +// ALOGI(" data @%zu [0x%X]", index, data[index]); +// } +//} /* * Device Functions */ -status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr) { +/** + * Retrieves information for the native MIDI device. + * + * device The Native API token for the device. This value is obtained from the + * AMidiDevice_fromJava(). + * outDeviceInfoPtr Receives the associated device info. + * + * Returns AMEDIA_OK or a negative error code. + * - AMEDIA_ERROR_INVALID_PARAMETER + * AMEDIA_ERROR_UNKNOWN + */ +static media_status_t AMIDI_API AMIDI_getDeviceInfo(const AMidiDevice *device, + AMidiDeviceInfo *outDeviceInfoPtr) { + if (device == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } + MidiDeviceInfo deviceInfo; Status txResult = device->server->getDeviceInfo(&deviceInfo); if (!txResult.isOk()) { ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError()); - return txResult.transactionError(); + return AMEDIA_ERROR_UNKNOWN; + } + + outDeviceInfoPtr->type = deviceInfo.getType(); + outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size(); + outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size(); + + return AMEDIA_OK; +} + +media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj, + AMidiDevice** devicePtrPtr) +{ + // Ensures JNI initialization is performed just once. + static std::once_flag initCallFlag; + std::call_once(initCallFlag, AMIDI_initJNI, env); + + if (j_midiDeviceObj == nullptr) { + ALOGE("AMidiDevice_fromJava() invalid MidiDevice object."); + return AMEDIA_ERROR_INVALID_OBJECT; + } + + { + std::lock_guard<std::mutex> guard(openMutex); + + long handle = env->GetLongField(j_midiDeviceObj, fidNativeHandle); + if (handle != 0) { + // Already opened by someone. + return AMEDIA_ERROR_INVALID_OBJECT; + } + + jobject serverBinderObj = env->GetObjectField(j_midiDeviceObj, fidDeviceServerBinder); + sp<IBinder> serverBinder = android::ibinderForJavaObject(env, serverBinderObj); + if (serverBinder.get() == nullptr) { + ALOGE("AMidiDevice_fromJava couldn't connect to native MIDI server."); + return AMEDIA_ERROR_UNKNOWN; + } + + // don't check allocation failures, just abort.. + AMidiDevice* devicePtr = new AMidiDevice; + devicePtr->server = new BpMidiDeviceServer(serverBinder); + jobject midiDeviceInfoObj = env->GetObjectField(j_midiDeviceObj, fidDeviceInfo); + devicePtr->deviceId = env->GetIntField(midiDeviceInfoObj, fidDeviceId); + + // Synchronize with the associated Java MidiDevice. + env->SetLongField(j_midiDeviceObj, fidNativeHandle, (long)devicePtr); + env->GetJavaVM(&devicePtr->javaVM); + devicePtr->midiDeviceObj = env->NewGlobalRef(j_midiDeviceObj); + + if (AMIDI_getDeviceInfo(devicePtr, &devicePtr->deviceInfo) != AMEDIA_OK) { + // This is weird, but maybe not fatal? + ALOGE("AMidiDevice_fromJava couldn't retrieve attributes of native device."); + } + + *devicePtrPtr = devicePtr; + } + + return AMEDIA_OK; +} + +media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device) +{ + if (device == nullptr || device->midiDeviceObj == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } + + JNIEnv* env; + jint err = device->javaVM->GetEnv((void**)&env, JNI_VERSION_1_6); + LOG_ALWAYS_FATAL_IF(err != JNI_OK, "AMidiDevice_release Error accessing JNIEnv err:%d", err); + + // Synchronize with the associated Java MidiDevice. + // env->CallVoidMethod(j_midiDeviceObj, midClearNativeHandle); + { + std::lock_guard<std::mutex> guard(openMutex); + long handle = env->GetLongField(device->midiDeviceObj, fidNativeHandle); + if (handle == 0) { + // Not opened as native. + ALOGE("AMidiDevice_release() device not opened in native client."); + return AMEDIA_ERROR_INVALID_OBJECT; + } + + env->SetLongField(device->midiDeviceObj, fidNativeHandle, 0L); } + env->DeleteGlobalRef(device->midiDeviceObj); + + delete device; - deviceInfoPtr->type = deviceInfo.getType(); - deviceInfoPtr->uid = deviceInfo.getUid(); - deviceInfoPtr->isPrivate = deviceInfo.isPrivate(); - deviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size(); - deviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size(); + return AMEDIA_OK; +} - return OK; +int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) { + if (device == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } + return device->deviceInfo.type; +} + +ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) { + if (device == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } + return device->deviceInfo.inputPortCount; +} + +ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) { + if (device == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } + return device->deviceInfo.outputPortCount; } /* * Port Helpers */ -static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type, +static media_status_t AMIDI_openPort(const AMidiDevice *device, int32_t portNumber, int type, AMIDI_Port **portPtr) { + if (device == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } + sp<BBinder> portToken(new BBinder()); unique_fd ufd; Status txResult = type == PORTTYPE_OUTPUT @@ -109,10 +276,10 @@ static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type, : device->server->openInputPort(portToken, portNumber, &ufd); if (!txResult.isOk()) { ALOGE("AMIDI_openPort transaction error: %d", txResult.transactionError()); - return txResult.transactionError(); + return AMEDIA_ERROR_UNKNOWN; } - AMIDI_Port* port = new AMIDI_Port; + AMIDI_Port *port = new AMIDI_Port; port->state = MIDI_PORT_STATE_OPEN_IDLE; port->device = device; port->binderToken = portToken; @@ -120,155 +287,178 @@ static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type, *portPtr = port; - return OK; + return AMEDIA_OK; } -static status_t AMIDI_closePort(AMIDI_Port *port) { +static void AMIDI_closePort(AMIDI_Port *port) { + if (port == nullptr) { + return; + } + int portState = MIDI_PORT_STATE_OPEN_IDLE; while (!port->state.compare_exchange_weak(portState, MIDI_PORT_STATE_CLOSED)) { if (portState == MIDI_PORT_STATE_CLOSED) { - return -EINVAL; // Already closed + return; // Already closed } } Status txResult = port->device->server->closePort(port->binderToken); if (!txResult.isOk()) { - return txResult.transactionError(); + ALOGE("Transaction error closing MIDI port:%d", txResult.transactionError()); } delete port; - - return OK; } /* * Output (receiving) API */ -status_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber, - AMIDI_OutputPort **outputPortPtr) { - return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outputPortPtr); +media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber, + AMidiOutputPort **outOutputPortPtr) { + return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outOutputPortPtr); } -ssize_t AMIDI_receive(AMIDI_OutputPort *outputPort, AMIDI_Message *messages, ssize_t maxMessages) { - AMIDI_Port *port = (AMIDI_Port*)outputPort; - int portState = MIDI_PORT_STATE_OPEN_IDLE; - if (!port->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) { - // The port has been closed. - return -EPIPE; +/* + * A little RAII (https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization) + * class to ensure that the port state is correct irrespective of errors. + */ +class MidiReceiver { +public: + MidiReceiver(AMIDI_Port *port) : mPort(port) {} + + ~MidiReceiver() { + // flag the port state to idle + mPort->state.store(MIDI_PORT_STATE_OPEN_IDLE); } - status_t result = OK; - ssize_t messagesRead = 0; - while (messagesRead < maxMessages) { - struct pollfd checkFds[1] = { { port->ufd, POLLIN, 0 } }; - int pollResult = poll(checkFds, 1, 0); - if (pollResult < 1) { - result = android::INVALID_OPERATION; - break; + ssize_t receive(int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes, + size_t *numBytesReceivedPtr, int64_t *timestampPtr) { + int portState = MIDI_PORT_STATE_OPEN_IDLE; + // check to see if the port is idle, then set to active + if (!mPort->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) { + // The port not idle or has been closed. + return AMEDIA_ERROR_UNKNOWN; } - AMIDI_Message *message = &messages[messagesRead]; - uint8_t readBuffer[AMIDI_PACKET_SIZE]; - memset(readBuffer, 0, sizeof(readBuffer)); - ssize_t readCount = read(port->ufd, readBuffer, sizeof(readBuffer)); - if (readCount == EINTR) { - continue; + struct pollfd checkFds[1] = { { mPort->ufd, POLLIN, 0 } }; + if (poll(checkFds, 1, 0) < 1) { + // Nothing there + return 0; } - if (readCount < 1) { - result = android::NOT_ENOUGH_DATA; - break; + + uint8_t readBuffer[AMIDI_PACKET_SIZE]; + ssize_t readCount = read(mPort->ufd, readBuffer, sizeof(readBuffer)); + if (readCount == EINTR || readCount < 1) { + return AMEDIA_ERROR_UNKNOWN; } - // set Packet Format definition at the top of this file. - size_t dataSize = 0; - message->opcode = readBuffer[0]; - message->timestamp = 0; - if (message->opcode == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) { - dataSize = readCount - AMIDI_PACKET_OVERHEAD; - if (dataSize) { - memcpy(message->buffer, readBuffer + 1, dataSize); + // see Packet Format definition at the top of this file. + size_t numMessageBytes = 0; + *opcodePtr = readBuffer[0]; + if (*opcodePtr == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) { + numMessageBytes = readCount - AMIDI_PACKET_OVERHEAD; + numMessageBytes = std::min(maxBytes, numMessageBytes); + memcpy(buffer, readBuffer + 1, numMessageBytes); + if (timestampPtr != nullptr) { + *timestampPtr = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t)); } - message->timestamp = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t)); } - message->len = dataSize; - ++messagesRead; + *numBytesReceivedPtr = numMessageBytes; + return 1; } - port->state.store(MIDI_PORT_STATE_OPEN_IDLE); +private: + AMIDI_Port *mPort; +}; + +ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr, + uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *timestampPtr) { + + if (outputPort == nullptr || buffer == nullptr) { + return -EINVAL; + } - return result == OK ? messagesRead : result; + return MidiReceiver((AMIDI_Port*)outputPort).receive(opcodePtr, buffer, maxBytes, + numBytesReceivedPtr, timestampPtr); } -status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort) { - return AMIDI_closePort((AMIDI_Port*)outputPort); +void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) { + AMIDI_closePort((AMIDI_Port*)outputPort); } /* * Input (sending) API */ -status_t AMIDI_openInputPort(AMIDI_Device *device, int portNumber, AMIDI_InputPort **inputPortPtr) { - return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)inputPortPtr); -} - -status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort) { - return AMIDI_closePort((AMIDI_Port*)inputPort); +media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber, + AMidiInputPort **outInputPortPtr) { + return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)outInputPortPtr); } -ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort */*inputPort*/) { - return SIZE_MIDIRECEIVEBUFFER; +void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) { + AMIDI_closePort((AMIDI_Port*)inputPort); } static ssize_t AMIDI_makeSendBuffer( - uint8_t *buffer, uint8_t *data, ssize_t numBytes,uint64_t timestamp) { + uint8_t *buffer, const uint8_t *data, size_t numBytes, uint64_t timestamp) { + // Error checking will happen in the caller since this isn't an API function. buffer[0] = AMIDI_OPCODE_DATA; memcpy(buffer + 1, data, numBytes); memcpy(buffer + 1 + numBytes, ×tamp, sizeof(timestamp)); return numBytes + AMIDI_PACKET_OVERHEAD; } -// Handy debugging function. -//static void AMIDI_logBuffer(uint8_t *data, size_t numBytes) { -// for (size_t index = 0; index < numBytes; index++) { -// ALOGI(" data @%zu [0x%X]", index, data[index]); -// } -//} - -ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes) { - return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0); +ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer, + size_t numBytes) { + return AMidiInputPort_sendWithTimestamp(inputPort, buffer, numBytes, 0); } -ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *data, - ssize_t numBytes, int64_t timestamp) { - - if (numBytes > SIZE_MIDIRECEIVEBUFFER) { - return android::BAD_VALUE; +ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, + const uint8_t *data, size_t numBytes, int64_t timestamp) { + if (inputPort == nullptr || data == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; } // AMIDI_logBuffer(data, numBytes); - uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD]; - ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp); - ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes); + uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD]; + size_t numSent = 0; + while (numSent < numBytes) { + size_t blockSize = AMIDI_BUFFER_SIZE; + blockSize = std::min(blockSize, numBytes - numSent); + + ssize_t numTransferBytes = + AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp); + ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes); + if (numWritten < 0) { + break; // error so bail out. + } + if (numWritten < numTransferBytes) { + ALOGE("AMidiInputPort_sendWithTimestamp Couldn't write MIDI data buffer." + " requested:%zu, written%zu",numTransferBytes, numWritten); + break; // bail + } - if (numWritten < numTransferBytes) { - ALOGE("AMIDI_sendWithTimestamp Couldn't write MIDI data buffer. requested:%zu, written%zu", - numTransferBytes, numWritten); + numSent += numWritten - AMIDI_PACKET_OVERHEAD; } - return numWritten - AMIDI_PACKET_OVERHEAD; + return numSent; } -status_t AMIDI_flush(AMIDI_InputPort *inputPort) { +media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) { + if (inputPort == nullptr) { + return AMEDIA_ERROR_INVALID_PARAMETER; + } + uint8_t opCode = AMIDI_OPCODE_FLUSH; ssize_t numTransferBytes = 1; ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes); if (numWritten < numTransferBytes) { - ALOGE("AMIDI_flush Couldn't write MIDI flush. requested:%zu, written%zu", + ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd", numTransferBytes, numWritten); - return android::INVALID_OPERATION; + return AMEDIA_ERROR_UNSUPPORTED; } - return OK; + return AMEDIA_OK; } diff --git a/media/native/midi/midi.h b/media/native/midi/midi.h deleted file mode 100644 index 9332b44dddd2..000000000000 --- a/media/native/midi/midi.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#ifndef ANDROID_MEDIA_MIDI_H_ -#define ANDROID_MEDIA_MIDI_H_ - -#include <stdarg.h> -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Errors.h> - -using android::status_t; - -#ifdef __cplusplus -extern "C" { -#endif - -struct AMIDI_Device; -struct AMIDI_InputPort; -struct AMIDI_OutputPort; - -#define AMIDI_INVALID_HANDLE NULL - -enum { - AMIDI_OPCODE_DATA = 1, - AMIDI_OPCODE_FLUSH = 2, - AMIDI_PACKET_SIZE = 1024, /* !!! Currently MidiPortImpl.MAX_PACKET_SIZE !!! */ - AMIDI_PACKET_OVERHEAD = 9, - AMIDI_BUFFER_SIZE = AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD - /* !!! TBD, currently MidiPortImpl.MAX_PACKET_DATA_SIZE !!! */ -}; - -typedef struct { - uint32_t opcode; - uint8_t buffer[AMIDI_BUFFER_SIZE]; - size_t len; - int64_t timestamp; -} AMIDI_Message; - -enum { - AMIDI_DEVICE_TYPE_USB = 1, - AMIDI_DEVICE_TYPE_VIRTUAL = 2, - AMIDI_DEVICE_TYPE_BLUETOOTH = 3 -}; - -typedef struct { - int32_t type; - int32_t uid; - int32_t isPrivate; - int32_t inputPortCount; - int32_t outputPortCount; -} AMIDI_DeviceInfo; - -/* - * Device API - */ -/* - * Retrieves information for the native MIDI device. - * - * device The Native API token for the device. - * deviceInfoPtr Receives the associated device info. - * - * Returns OK or a (negative) error code. - */ -status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr); - -/* - * API for receiving data from the Output port of a device. - */ -/* - * Opens the output port. - * - * device Identifies the device. - * portNumber Specifies the zero-based port index on the device to open. - * outputPortPtr Receives the native API port identifier of the opened port. - * - * Returns OK, or a (negative) error code. - */ -status_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber, - AMIDI_OutputPort **outputPortPtr); - -/* - * Receives any pending MIDI messages (up to the specified maximum number of messages). - * - * outputPort Identifies the port to receive messages from. - * messages Points to an array (size maxMessages) to receive the MIDI messages. - * maxMessages The number of messages allocated in the messages array. - * - * Returns the number of messages received, or a (negative) error code. - */ -ssize_t AMIDI_receive(AMIDI_OutputPort *outputPort, AMIDI_Message *messages, ssize_t maxMessages); - -/* - * Closes the output port. - * - * outputPort The native API port identifier of the port. - * - * Returns OK, or a (negative) error code. - */ -status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort); - -/* - * API for sending data to the Input port of a device. - */ -/* - * Opens the input port. - * - * device Identifies the device. - * portNumber Specifies the zero-based port index on the device to open. - * inputPortPtr Receives the native API port identifier of the opened port. - * - * Returns OK, or a (negative) error code. - */ -status_t AMIDI_openInputPort(AMIDI_Device *device, int portNumber, AMIDI_InputPort **inputPortPtr); - -/* - * Returns the maximum number of bytes that can be received in a single MIDI message. - */ -ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort *inputPort); - -/* - * Sends data to the specified input port. - * - * inputPort The native API identifier of the port to send data to. - * buffer Points to the array of bytes containing the data to send. - * numBytes Specifies the number of bytes to write. - * - * Returns The number of bytes sent or a (negative) error code. - */ -ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes); - -/* - * Sends data to the specified input port with a timestamp. - * - * inputPort The native API identifier of the port to send data to. - * buffer Points to the array of bytes containing the data to send. - * numBytes Specifies the number of bytes to write. - * timestamp The time stamp to associate with the sent data. - * - * Returns The number of bytes sent or a (negative) error code. - */ -ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *buffer, - ssize_t numBytes, int64_t timestamp); - -/* - * Sends a message with a 'MIDI flush command code' to the specified port. - * - * inputPort The native API identifier of the port to send the flush message to. - * - * Returns OK, or a (negative) error code. - */ -status_t AMIDI_flush(AMIDI_InputPort *inputPort); - -/* - * Closes the input port. - * - * inputPort The native API port identifier of the port. - * - * - * Returns OK, or a (negative) error code. - */ -status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort); - -#ifdef __cplusplus -} -#endif - -#endif /* ANDROID_MEDIA_MIDI_H_ */ diff --git a/media/native/midi/midi_internal.h b/media/native/midi/midi_internal.h index fd4770e05d6c..cb3ecce13533 100644 --- a/media/native/midi/midi_internal.h +++ b/media/native/midi/midi_internal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2018 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. @@ -17,11 +17,25 @@ #ifndef ANDROID_MEDIA_MIDI_INTERNAL_H_ #define ANDROID_MEDIA_MIDI_INTERNAL_H_ +#include <jni.h> + #include "android/media/midi/BpMidiDeviceServer.h" -struct AMIDI_Device { - android::sp<android::media::midi::BpMidiDeviceServer> server; - int32_t deviceId; +typedef struct { + int32_t type; /* one of AMIDI_DEVICE_TYPE_* constants */ + int32_t inputPortCount; /* number of input (send) ports associated with the device */ + int32_t outputPortCount; /* number of output (received) ports associated with the device */ +} AMidiDeviceInfo; + +struct AMidiDevice { + android::sp<android::media::midi::BpMidiDeviceServer> + server; /* The Binder interface to the MIDI server (from the Java MidiDevice) */ + int32_t deviceId; /* The integer id of the device assigned in the Java API */ + JavaVM* javaVM; /* The Java VM (so we can obtain the JNIEnv in the + AMidiDevice_close function) */ + jobject midiDeviceObj; /* NewGlobalRef() reference to the Java MidiDevice associated with + this native AMidiDevice. */ + AMidiDeviceInfo deviceInfo; /* Attributes of the device. */ }; #endif // ANDROID_MEDIA_MIDI_INTERNAL_H_ diff --git a/media/tests/NativeMidiDemo/Android.mk b/media/tests/NativeMidiDemo/Android.mk deleted file mode 100644 index 316858f667fd..000000000000 --- a/media/tests/NativeMidiDemo/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2016 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_PACKAGE_NAME := NativeMidiDemo - -#LOCAL_SDK_VERSION := current -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_PROGUARD_ENABLED := disabled -LOCAL_SRC_FILES := $(call all-java-files-under, java) - -LOCAL_JNI_SHARED_LIBRARIES := libnativemidi_jni - -include $(BUILD_PACKAGE) - -# Include packages in subdirectories -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/tests/NativeMidiDemo/AndroidManifest.xml b/media/tests/NativeMidiDemo/AndroidManifest.xml deleted file mode 100644 index 322873f11895..000000000000 --- a/media/tests/NativeMidiDemo/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.android.nativemididemo" - android:versionCode="1" - android:versionName="1.0"> - <application - android:allowBackup="false" - android:fullBackupContent="false" - android:icon="@mipmap/ic_launcher" - android:label="@string/app_name"> - <uses-feature android:name="android.software.midi" android:required="true"/> - <activity android:name=".NativeMidi" - android:label="@string/app_name"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/NativeMidi.java b/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/NativeMidi.java deleted file mode 100644 index b0ca0bb74548..000000000000 --- a/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/NativeMidi.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (C) 2016 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.nativemididemo; - -import android.app.Activity; -import android.content.Context; -import android.media.midi.MidiDevice; -import android.media.midi.MidiDeviceInfo; -import android.media.midi.MidiManager; -import android.media.midi.MidiOutputPort; -import android.media.midi.MidiReceiver; -import android.media.AudioManager; -import android.os.Bundle; -import android.os.Handler; -import android.view.View; -import android.view.WindowManager; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.TextView; - -import java.io.IOException; - -public class NativeMidi extends Activity -{ - private TextView mCallbackStatusTextView; - private TextView mJavaMidiStatusTextView; - private TextView mMessagesTextView; - private RadioGroup mMidiDevicesRadioGroup; - private Handler mTimerHandler = new Handler(); - private boolean mAudioWorks; - private final int mMinFramesPerBuffer = 32; // See {min|max}PlaySamples in nativemidi-jni.cpp - private final int mMaxFramesPerBuffer = 1000; - private int mFramesPerBuffer; - - private TouchableScrollView mMessagesContainer; - private MidiManager mMidiManager; - private MidiOutputPortSelector mActivePortSelector; - - private Runnable mTimerRunnable = new Runnable() { - private long mLastTime; - private long mLastPlaybackCounter; - private int mLastCallbackRate; - private long mLastUntouchedTime; - - @Override - public void run() { - final long checkIntervalMs = 1000; - long currentTime = System.currentTimeMillis(); - long currentPlaybackCounter = getPlaybackCounter(); - if (currentTime - mLastTime >= checkIntervalMs) { - int callbackRate = Math.round( - (float)(currentPlaybackCounter - mLastPlaybackCounter) / - ((float)(currentTime - mLastTime) / (float)1000)); - if (mLastCallbackRate != callbackRate) { - mCallbackStatusTextView.setText( - "CB: " + callbackRate + " Hz"); - mLastCallbackRate = callbackRate; - } - mLastTime = currentTime; - mLastPlaybackCounter = currentPlaybackCounter; - } - - String[] newMessages = getRecentMessages(); - if (newMessages != null) { - for (String message : newMessages) { - mMessagesTextView.append(message); - mMessagesTextView.append("\n"); - } - if (!mMessagesContainer.isTouched) { - if (mLastUntouchedTime == 0) mLastUntouchedTime = currentTime; - if (currentTime - mLastUntouchedTime > 3000) { - mMessagesContainer.fullScroll(View.FOCUS_DOWN); - } - } else { - mLastUntouchedTime = 0; - } - } - - mTimerHandler.postDelayed(this, checkIntervalMs / 4); - } - }; - - private void addMessage(final String message) { - mTimerHandler.postDelayed(new Runnable() { - @Override - public void run() { - mMessagesTextView.append(message); - } - }, 0); - } - - private class MidiOutputPortSelector implements View.OnClickListener { - private final MidiDeviceInfo mDeviceInfo; - private final int mPortNumber; - private MidiDevice mDevice; - private MidiOutputPort mOutputPort; - - MidiOutputPortSelector() { - mDeviceInfo = null; - mPortNumber = -1; - } - - MidiOutputPortSelector(MidiDeviceInfo info, int portNumber) { - mDeviceInfo = info; - mPortNumber = portNumber; - } - - MidiDeviceInfo getDeviceInfo() { return mDeviceInfo; } - - @Override - public void onClick(View v) { - if (mActivePortSelector != null) { - mActivePortSelector.close(); - mActivePortSelector = null; - } - if (mDeviceInfo == null) { - mActivePortSelector = this; - return; - } - mMidiManager.openDevice(mDeviceInfo, new MidiManager.OnDeviceOpenedListener() { - @Override - public void onDeviceOpened(MidiDevice device) { - if (device == null) { - addMessage("! Failed to open MIDI device !\n"); - } else { - mDevice = device; - try { - mDevice.mirrorToNative(); - startReadingMidi(mDevice.getInfo().getId(), mPortNumber); - } catch (IOException e) { - addMessage("! Failed to mirror to native !\n" + e.getMessage() + "\n"); - } - - mActivePortSelector = MidiOutputPortSelector.this; - - mOutputPort = device.openOutputPort(mPortNumber); - mOutputPort.connect(mMidiReceiver); - } - } - }, null); - } - - void closePortOnly() { - stopReadingMidi(); - } - - void close() { - closePortOnly(); - try { - if (mOutputPort != null) { - mOutputPort.close(); - } - } catch (IOException e) { - mMessagesTextView.append("! Port close error: " + e + "\n"); - } finally { - mOutputPort = null; - } - try { - if (mDevice != null) { - mDevice.close(); - } - } catch (IOException e) { - mMessagesTextView.append("! Device close error: " + e + "\n"); - } finally { - mDevice = null; - } - } - } - - private MidiManager.DeviceCallback mMidiDeviceCallback = new MidiManager.DeviceCallback() { - @Override - public void onDeviceAdded(MidiDeviceInfo info) { - Bundle deviceProps = info.getProperties(); - String deviceName = deviceProps.getString(MidiDeviceInfo.PROPERTY_NAME); - if (deviceName == null) { - deviceName = deviceProps.getString(MidiDeviceInfo.PROPERTY_MANUFACTURER); - } - - for (MidiDeviceInfo.PortInfo port : info.getPorts()) { - if (port.getType() != MidiDeviceInfo.PortInfo.TYPE_OUTPUT) continue; - String portName = port.getName(); - int portNumber = port.getPortNumber(); - if (portName.length() == 0) portName = "[" + portNumber + "]"; - portName += "@" + deviceName; - RadioButton outputDevice = new RadioButton(NativeMidi.this); - outputDevice.setText(portName); - outputDevice.setTag(info); - outputDevice.setOnClickListener(new MidiOutputPortSelector(info, portNumber)); - mMidiDevicesRadioGroup.addView(outputDevice); - } - - NativeMidi.this.updateKeepScreenOn(); - } - - @Override - public void onDeviceRemoved(MidiDeviceInfo info) { - if (mActivePortSelector != null && info.equals(mActivePortSelector.getDeviceInfo())) { - mActivePortSelector.close(); - mActivePortSelector = null; - } - int removeButtonStart = -1, removeButtonCount = 0; - final int buttonCount = mMidiDevicesRadioGroup.getChildCount(); - boolean checked = false; - for (int i = 0; i < buttonCount; ++i) { - RadioButton button = (RadioButton) mMidiDevicesRadioGroup.getChildAt(i); - if (!info.equals(button.getTag())) continue; - if (removeButtonStart == -1) removeButtonStart = i; - ++removeButtonCount; - if (button.isChecked()) checked = true; - } - if (removeButtonStart != -1) { - mMidiDevicesRadioGroup.removeViews(removeButtonStart, removeButtonCount); - if (checked) { - mMidiDevicesRadioGroup.check(R.id.device_none); - } - } - - NativeMidi.this.updateKeepScreenOn(); - } - }; - - private class JavaMidiReceiver extends MidiReceiver implements Runnable { - @Override - public void onSend(byte[] data, int offset, - int count, long timestamp) throws IOException { - mTimerHandler.removeCallbacks(this); - mTimerHandler.postDelayed(new Runnable() { - @Override - public void run() { - mJavaMidiStatusTextView.setText("Java: MSG"); - } - }, 0); - mTimerHandler.postDelayed(this, 100); - } - - @Override - public void run() { - mJavaMidiStatusTextView.setText("Java: ---"); - } - } - - private JavaMidiReceiver mMidiReceiver = new JavaMidiReceiver(); - - private void updateKeepScreenOn() { - if (mMidiDevicesRadioGroup.getChildCount() > 1) { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } else { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - mCallbackStatusTextView = findViewById(R.id.callback_status); - mJavaMidiStatusTextView = findViewById(R.id.java_midi_status); - mMessagesTextView = findViewById(R.id.messages); - mMessagesContainer = findViewById(R.id.messages_scroll); - mMidiDevicesRadioGroup = findViewById(R.id.devices); - RadioButton deviceNone = findViewById(R.id.device_none); - deviceNone.setOnClickListener(new MidiOutputPortSelector()); - - AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); - if (sampleRate == null) sampleRate = "48000"; - String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); - if (framesPerBuffer == null) framesPerBuffer = Integer.toString(mMaxFramesPerBuffer); - mFramesPerBuffer = Integer.parseInt(framesPerBuffer); - String audioInitResult = initAudio(Integer.parseInt(sampleRate), mFramesPerBuffer); - mMessagesTextView.append("Open SL ES init: " + audioInitResult + "\n"); - - if (audioInitResult.startsWith("Success")) { - mAudioWorks = true; - mTimerHandler.postDelayed(mTimerRunnable, 0); - mTimerHandler.postDelayed(mMidiReceiver, 0); - } - - mMidiManager = (MidiManager) getSystemService(Context.MIDI_SERVICE); - mMidiManager.registerDeviceCallback(mMidiDeviceCallback, new Handler()); - for (MidiDeviceInfo info : mMidiManager.getDevices()) { - mMidiDeviceCallback.onDeviceAdded(info); - } - } - - @Override - public void onPause() { - super.onPause(); - if (mAudioWorks) { - mTimerHandler.removeCallbacks(mTimerRunnable); - pauseAudio(); - } - } - - @Override - public void onResume() { - super.onResume(); - if (mAudioWorks) { - mTimerHandler.postDelayed(mTimerRunnable, 0); - resumeAudio(); - } - } - - @Override - protected void onDestroy() { - if (mActivePortSelector != null) { - mActivePortSelector.close(); - mActivePortSelector = null; - } - shutdownAudio(); - super.onDestroy(); - } - - public void onClearMessages(View v) { - mMessagesTextView.setText(""); - } - - public void onClosePort(View v) { - if (mActivePortSelector != null) { - mActivePortSelector.closePortOnly(); - } - } - - private native String initAudio(int sampleRate, int playSamples); - private native void pauseAudio(); - private native void resumeAudio(); - private native void shutdownAudio(); - - private native long getPlaybackCounter(); - private native String[] getRecentMessages(); - - private native void startReadingMidi(int deviceId, int portNumber); - private native void stopReadingMidi(); - - static { - System.loadLibrary("nativemidi_jni"); - } -} diff --git a/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/TouchableScrollView.java b/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/TouchableScrollView.java deleted file mode 100644 index 645aafa88507..000000000000 --- a/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/TouchableScrollView.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.example.android.nativemididemo; - -import android.content.Context; -import android.view.MotionEvent; -import android.util.AttributeSet; -import android.widget.ScrollView; - -public class TouchableScrollView extends ScrollView { - public boolean isTouched; - - public TouchableScrollView(Context context) { - super(context); - } - - public TouchableScrollView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - isTouched = true; - break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - isTouched = false; - break; - } - return super.onTouchEvent(event); - } -} diff --git a/media/tests/NativeMidiDemo/jni/Android.mk b/media/tests/NativeMidiDemo/jni/Android.mk deleted file mode 100644 index 69a64bd0218b..000000000000 --- a/media/tests/NativeMidiDemo/jni/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) 2016 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_MODULE := libnativemidi_jni - -LOCAL_SRC_FILES := \ - nativemidi-jni.cpp \ - messagequeue.cpp - -LOCAL_CFLAGS += -Wall -Wextra -Werror -O0 - -LOCAL_C_INCLUDES += \ - frameworks/base/media/native - -LOCAL_CXX_STL := libc++_static - -LOCAL_SHARED_LIBRARIES := libOpenSLES libmidi - -include $(BUILD_SHARED_LIBRARY) diff --git a/media/tests/NativeMidiDemo/jni/messagequeue.cpp b/media/tests/NativeMidiDemo/jni/messagequeue.cpp deleted file mode 100644 index ffaef38bed8c..000000000000 --- a/media/tests/NativeMidiDemo/jni/messagequeue.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2016 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. - * - */ - -#include <atomic> -#include <stdio.h> -#include <string.h> - -#include "messagequeue.h" - -namespace nativemididemo { - -static const int messageBufferSize = 64 * 1024; -static char messageBuffer[messageBufferSize]; -static std::atomic_ullong messagesLastWritePosition; - -void writeMessage(const char* message) -{ - static unsigned long long lastWritePos = 0; - size_t messageLen = strlen(message); - if (messageLen == 0) return; - - messageLen += 1; // Also count in the null terminator. - char buffer[1024]; - if (messageLen >= messageBufferSize) { - snprintf(buffer, sizeof(buffer), "!!! Message too long: %zu bytes !!!", messageLen); - message = buffer; - messageLen = strlen(message); - } - - size_t wrappedWritePos = lastWritePos % messageBufferSize; - if (wrappedWritePos + messageLen >= messageBufferSize) { - size_t tailLen = messageBufferSize - wrappedWritePos; - memset(messageBuffer + wrappedWritePos, 0, tailLen); - lastWritePos += tailLen; - wrappedWritePos = 0; - } - - memcpy(messageBuffer + wrappedWritePos, message, messageLen); - lastWritePos += messageLen; - messagesLastWritePosition.store(lastWritePos); -} - -static char messageBufferCopy[messageBufferSize]; - -jobjectArray getRecentMessagesForJava(JNIEnv* env, jobject) -{ - static unsigned long long lastReadPos = 0; - const char* overrunMessage = ""; - size_t messagesCount = 0; - jobjectArray result = NULL; - - // First we copy the portion of the message buffer into messageBufferCopy. If after finishing - // the copy we notice that the writer has mutated the portion of the buffer that we were - // copying, we report an overrun. Afterwards we can safely read messages from the copy. - memset(messageBufferCopy, 0, sizeof(messageBufferCopy)); - unsigned long long lastWritePos = messagesLastWritePosition.load(); - if (lastWritePos - lastReadPos > messageBufferSize) { - overrunMessage = "!!! Message buffer overrun !!!"; - messagesCount = 1; - lastReadPos = lastWritePos; - goto create_array; - } - if (lastWritePos == lastReadPos) return result; - if (lastWritePos / messageBufferSize == lastReadPos / messageBufferSize) { - size_t wrappedReadPos = lastReadPos % messageBufferSize; - memcpy(messageBufferCopy + wrappedReadPos, - messageBuffer + wrappedReadPos, - lastWritePos % messageBufferSize - wrappedReadPos); - } else { - size_t wrappedReadPos = lastReadPos % messageBufferSize; - memcpy(messageBufferCopy, messageBuffer, lastWritePos % messageBufferSize); - memcpy(messageBufferCopy + wrappedReadPos, - messageBuffer + wrappedReadPos, - messageBufferSize - wrappedReadPos); - } - { - unsigned long long newLastWritePos = messagesLastWritePosition.load(); - if (newLastWritePos - lastReadPos > messageBufferSize) { - overrunMessage = "!!! Message buffer overrun !!!"; - messagesCount = 1; - lastReadPos = lastWritePos = newLastWritePos; - goto create_array; - } - } - // Otherwise we ignore newLastWritePos, since we only have a copy of the buffer - // up to lastWritePos. - - for (unsigned long long readPos = lastReadPos; readPos < lastWritePos; ) { - size_t messageLen = strlen(messageBufferCopy + (readPos % messageBufferSize)); - if (messageLen != 0) { - readPos += messageLen + 1; - messagesCount++; - } else { - // Skip to the beginning of the buffer. - readPos = (readPos / messageBufferSize + 1) * messageBufferSize; - } - } - if (messagesCount == 0) { - lastReadPos = lastWritePos; - return result; - } - -create_array: - result = env->NewObjectArray( - messagesCount, env->FindClass("java/lang/String"), env->NewStringUTF(overrunMessage)); - if (lastWritePos == lastReadPos) return result; - - jsize arrayIndex = 0; - while (lastReadPos < lastWritePos) { - size_t wrappedReadPos = lastReadPos % messageBufferSize; - if (messageBufferCopy[wrappedReadPos] != '\0') { - jstring message = env->NewStringUTF(messageBufferCopy + wrappedReadPos); - env->SetObjectArrayElement(result, arrayIndex++, message); - lastReadPos += env->GetStringLength(message) + 1; - env->DeleteLocalRef(message); - } else { - // Skip to the beginning of the buffer. - lastReadPos = (lastReadPos / messageBufferSize + 1) * messageBufferSize; - } - } - return result; -} - -} // namespace nativemididemo diff --git a/media/tests/NativeMidiDemo/jni/messagequeue.h b/media/tests/NativeMidiDemo/jni/messagequeue.h deleted file mode 100644 index 20aa9e805d12..000000000000 --- a/media/tests/NativeMidiDemo/jni/messagequeue.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2016 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. - * - */ - -#ifndef NATIVEMIDIDEMO_MESSAGEQUEUE_H -#define NATIVEMIDIDEMO_MESSAGEQUEUE_H - -#include <jni.h> - -namespace nativemididemo { - -void writeMessage(const char* message); -jobjectArray getRecentMessagesForJava(JNIEnv* env, jobject thiz); - -} // namespace nativemididemo - -#endif // NATIVEMIDIDEMO_MESSAGEQUEUE_H diff --git a/media/tests/NativeMidiDemo/jni/nativemidi-jni.cpp b/media/tests/NativeMidiDemo/jni/nativemidi-jni.cpp deleted file mode 100644 index 0110b75b7ba4..000000000000 --- a/media/tests/NativeMidiDemo/jni/nativemidi-jni.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include <atomic> -#include <inttypes.h> -#include <stdio.h> -#include <string.h> - -#include <jni.h> - -#include <midi/midi.h> -#include <SLES/OpenSLES.h> -#include <SLES/OpenSLES_Android.h> - -#include "messagequeue.h" - -extern "C" { -JNIEXPORT jstring JNICALL Java_com_example_android_nativemididemo_NativeMidi_initAudio( - JNIEnv* env, jobject thiz, jint sampleRate, jint playSamples); -JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_pauseAudio( - JNIEnv* env, jobject thiz); -JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_resumeAudio( - JNIEnv* env, jobject thiz); -JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_shutdownAudio( - JNIEnv* env, jobject thiz); -JNIEXPORT jlong JNICALL Java_com_example_android_nativemididemo_NativeMidi_getPlaybackCounter( - JNIEnv* env, jobject thiz); -JNIEXPORT jobjectArray JNICALL Java_com_example_android_nativemididemo_NativeMidi_getRecentMessages( - JNIEnv* env, jobject thiz); -JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi( - JNIEnv* env, jobject thiz, jint deviceId, jint portNumber); -JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_stopReadingMidi( - JNIEnv* env, jobject thiz); -} - -static const char* errStrings[] = { - "SL_RESULT_SUCCESS", // 0 - "SL_RESULT_PRECONDITIONS_VIOLATED", // 1 - "SL_RESULT_PARAMETER_INVALID", // 2 - "SL_RESULT_MEMORY_FAILURE", // 3 - "SL_RESULT_RESOURCE_ERROR", // 4 - "SL_RESULT_RESOURCE_LOST", // 5 - "SL_RESULT_IO_ERROR", // 6 - "SL_RESULT_BUFFER_INSUFFICIENT", // 7 - "SL_RESULT_CONTENT_CORRUPTED", // 8 - "SL_RESULT_CONTENT_UNSUPPORTED", // 9 - "SL_RESULT_CONTENT_NOT_FOUND", // 10 - "SL_RESULT_PERMISSION_DENIED", // 11 - "SL_RESULT_FEATURE_UNSUPPORTED", // 12 - "SL_RESULT_INTERNAL_ERROR", // 13 - "SL_RESULT_UNKNOWN_ERROR", // 14 - "SL_RESULT_OPERATION_ABORTED", // 15 - "SL_RESULT_CONTROL_LOST" }; // 16 -static const char* getSLErrStr(int code) { - return errStrings[code]; -} - -static SLObjectItf engineObject; -static SLEngineItf engineEngine; -static SLObjectItf outputMixObject; -static SLObjectItf playerObject; -static SLPlayItf playerPlay; -static SLAndroidSimpleBufferQueueItf playerBufferQueue; - -static const int minPlaySamples = 32; -static const int maxPlaySamples = 1000; -static std::atomic_int playSamples(maxPlaySamples); -static short playBuffer[maxPlaySamples]; - -static std::atomic_ullong sharedCounter; - -static AMIDI_Device* midiDevice = AMIDI_INVALID_HANDLE; -static std::atomic<AMIDI_OutputPort*> midiOutputPort(AMIDI_INVALID_HANDLE); - -static int setPlaySamples(int newPlaySamples) -{ - if (newPlaySamples < minPlaySamples) newPlaySamples = minPlaySamples; - if (newPlaySamples > maxPlaySamples) newPlaySamples = maxPlaySamples; - playSamples.store(newPlaySamples); - return newPlaySamples; -} - -// Amount of messages we are ready to handle during one callback cycle. -static const size_t MAX_INCOMING_MIDI_MESSAGES = 20; -// Static allocation to save time in the callback. -static AMIDI_Message incomingMidiMessages[MAX_INCOMING_MIDI_MESSAGES]; - -static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void */*context*/) -{ - sharedCounter++; - - AMIDI_OutputPort* outputPort = midiOutputPort.load(); - if (outputPort != AMIDI_INVALID_HANDLE) { - char midiDumpBuffer[1024]; - ssize_t midiReceived = AMIDI_receive( - outputPort, incomingMidiMessages, MAX_INCOMING_MIDI_MESSAGES); - if (midiReceived >= 0) { - for (ssize_t i = 0; i < midiReceived; ++i) { - AMIDI_Message* msg = &incomingMidiMessages[i]; - if (msg->opcode == AMIDI_OPCODE_DATA) { - memset(midiDumpBuffer, 0, sizeof(midiDumpBuffer)); - int pos = snprintf(midiDumpBuffer, sizeof(midiDumpBuffer), - "%" PRIx64 " ", msg->timestamp); - for (uint8_t *b = msg->buffer, *e = b + msg->len; b < e; ++b) { - pos += snprintf(midiDumpBuffer + pos, sizeof(midiDumpBuffer) - pos, - "%02x ", *b); - } - nativemididemo::writeMessage(midiDumpBuffer); - } else if (msg->opcode == AMIDI_OPCODE_FLUSH) { - nativemididemo::writeMessage("MIDI flush"); - } - } - } else { - snprintf(midiDumpBuffer, sizeof(midiDumpBuffer), - "! MIDI Receive error: %s !", strerror(-midiReceived)); - nativemididemo::writeMessage(midiDumpBuffer); - } - } - - size_t usedBufferSize = playSamples.load() * sizeof(playBuffer[0]); - if (usedBufferSize > sizeof(playBuffer)) { - usedBufferSize = sizeof(playBuffer); - } - (*bq)->Enqueue(bq, playBuffer, usedBufferSize); -} - -jstring Java_com_example_android_nativemididemo_NativeMidi_initAudio( - JNIEnv* env, jobject, jint sampleRate, jint playSamples) { - const char* stage; - SLresult result; - char printBuffer[1024]; - - playSamples = setPlaySamples(playSamples); - - result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); - if (SL_RESULT_SUCCESS != result) { stage = "slCreateEngine"; goto handle_error; } - - result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { stage = "realize Engine object"; goto handle_error; } - - result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); - if (SL_RESULT_SUCCESS != result) { stage = "get Engine interface"; goto handle_error; } - - result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL); - if (SL_RESULT_SUCCESS != result) { stage = "CreateOutputMix"; goto handle_error; } - - result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { stage = "realize OutputMix object"; goto handle_error; } - - { - SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, 1, (SLuint32)sampleRate * 1000, - SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, - SL_SPEAKER_FRONT_LEFT, SL_BYTEORDER_LITTLEENDIAN }; - SLDataLocator_AndroidSimpleBufferQueue loc_bufq = - { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; - SLDataSource audioSrc = { &loc_bufq, &format_pcm }; - SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject }; - SLDataSink audioSnk = { &loc_outmix, NULL }; - const SLInterfaceID ids[1] = { SL_IID_BUFFERQUEUE }; - const SLboolean req[1] = { SL_BOOLEAN_TRUE }; - result = (*engineEngine)->CreateAudioPlayer( - engineEngine, &playerObject, &audioSrc, &audioSnk, 1, ids, req); - if (SL_RESULT_SUCCESS != result) { stage = "CreateAudioPlayer"; goto handle_error; } - - result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { stage = "realize Player object"; goto handle_error; } - } - - result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay); - if (SL_RESULT_SUCCESS != result) { stage = "get Play interface"; goto handle_error; } - - result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue); - if (SL_RESULT_SUCCESS != result) { stage = "get BufferQueue interface"; goto handle_error; } - - result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, bqPlayerCallback, NULL); - if (SL_RESULT_SUCCESS != result) { stage = "register BufferQueue callback"; goto handle_error; } - - result = (*playerBufferQueue)->Enqueue(playerBufferQueue, playBuffer, sizeof(playBuffer)); - if (SL_RESULT_SUCCESS != result) { - stage = "enqueue into PlayerBufferQueue"; goto handle_error; } - - result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); - if (SL_RESULT_SUCCESS != result) { - stage = "SetPlayState(SL_PLAYSTATE_PLAYING)"; goto handle_error; } - - snprintf(printBuffer, sizeof(printBuffer), - "Success, sample rate %d, buffer samples %d", sampleRate, playSamples); - return env->NewStringUTF(printBuffer); - -handle_error: - snprintf(printBuffer, sizeof(printBuffer), "Error at %s: %s", stage, getSLErrStr(result)); - return env->NewStringUTF(printBuffer); -} - -void Java_com_example_android_nativemididemo_NativeMidi_pauseAudio( - JNIEnv*, jobject) { - if (playerPlay != NULL) { - (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PAUSED); - } -} - -void Java_com_example_android_nativemididemo_NativeMidi_resumeAudio( - JNIEnv*, jobject) { - if (playerBufferQueue != NULL && playerPlay != NULL) { - (*playerBufferQueue)->Enqueue(playerBufferQueue, playBuffer, sizeof(playBuffer)); - (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); - } -} - -void Java_com_example_android_nativemididemo_NativeMidi_shutdownAudio( - JNIEnv*, jobject) { - if (playerObject != NULL) { - (*playerObject)->Destroy(playerObject); - playerObject = NULL; - playerPlay = NULL; - playerBufferQueue = NULL; - } - - if (outputMixObject != NULL) { - (*outputMixObject)->Destroy(outputMixObject); - outputMixObject = NULL; - } - - if (engineObject != NULL) { - (*engineObject)->Destroy(engineObject); - engineObject = NULL; - engineEngine = NULL; - } -} - -jlong Java_com_example_android_nativemididemo_NativeMidi_getPlaybackCounter(JNIEnv*, jobject) { - return sharedCounter.load(); -} - -jobjectArray Java_com_example_android_nativemididemo_NativeMidi_getRecentMessages( - JNIEnv* env, jobject thiz) { - return nativemididemo::getRecentMessagesForJava(env, thiz); -} - -void Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi( - JNIEnv*, jobject, jlong deviceHandle, jint portNumber) { - char buffer[1024]; - - midiDevice = (AMIDI_Device*)deviceHandle; -// int result = AMIDI_getDeviceById(deviceId, &midiDevice); -// if (result == 0) { -// snprintf(buffer, sizeof(buffer), "Obtained device token for uid %d: token %d", deviceId, midiDevice); -// } else { -// snprintf(buffer, sizeof(buffer), "Could not obtain device token for uid %d: %d", deviceId, result); -// } - nativemididemo::writeMessage(buffer); -// if (result) return; - - AMIDI_DeviceInfo deviceInfo; - int result = AMIDI_getDeviceInfo(midiDevice, &deviceInfo); - if (result == 0) { - snprintf(buffer, sizeof(buffer), "Device info: uid %d, type %d, priv %d, ports %d I / %d O", - deviceInfo.uid, deviceInfo.type, deviceInfo.isPrivate, - (int)deviceInfo.inputPortCount, (int)deviceInfo.outputPortCount); - } else { - snprintf(buffer, sizeof(buffer), "Could not obtain device info %d", result); - } - nativemididemo::writeMessage(buffer); - if (result) return; - - AMIDI_OutputPort* outputPort; - result = AMIDI_openOutputPort(midiDevice, portNumber, &outputPort); - if (result == 0) { - snprintf(buffer, sizeof(buffer), "Opened port %d: token %p", portNumber, outputPort); - midiOutputPort.store(outputPort); - } else { - snprintf(buffer, sizeof(buffer), "Could not open port %p: %d", midiDevice, result); - } - nativemididemo::writeMessage(buffer); -} - -void Java_com_example_android_nativemididemo_NativeMidi_stopReadingMidi( - JNIEnv*, jobject) { - AMIDI_OutputPort* outputPort = midiOutputPort.exchange(AMIDI_INVALID_HANDLE); - if (outputPort == AMIDI_INVALID_HANDLE) return; - int result = AMIDI_closeOutputPort(outputPort); - char buffer[1024]; - if (result == 0) { - snprintf(buffer, sizeof(buffer), "Closed port by token %p", outputPort); - } else { - snprintf(buffer, sizeof(buffer), "Could not close port by token %p: %d", outputPort, result); - } - nativemididemo::writeMessage(buffer); -} diff --git a/media/tests/NativeMidiDemo/res/layout/main.xml b/media/tests/NativeMidiDemo/res/layout/main.xml deleted file mode 100644 index 465d471a2de7..000000000000 --- a/media/tests/NativeMidiDemo/res/layout/main.xml +++ /dev/null @@ -1,93 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - > - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - > - <TextView - android:id="@+id/callback_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="13sp" - android:typeface="monospace" - /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="13sp" - android:typeface="monospace" - android:text=" | " - /> - <TextView - android:id="@+id/java_midi_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="13sp" - android:typeface="monospace" - /> - <TextView - android:layout_width="0px" - android:layout_height="wrap_content" - android:layout_weight="1" - android:textSize="13sp" - android:typeface="monospace" - android:text=" " - /> - </LinearLayout> - <HorizontalScrollView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:paddingTop="6sp" - android:paddingBottom="6sp" - > - <RadioGroup - android:id="@+id/devices" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:checkedButton="@+id/device_none" - > - <RadioButton - android:id="@+id/device_none" - android:text="None" - /> - </RadioGroup> - </HorizontalScrollView> - <com.example.android.nativemididemo.TouchableScrollView android:id="@+id/messages_scroll" - android:layout_width="match_parent" - android:layout_height="0px" - android:layout_weight="1" - > - <TextView android:id="@+id/messages" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="13sp" - android:typeface="monospace" - /> - </com.example.android.nativemididemo.TouchableScrollView> - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - > - <Button android:id="@+id/clear_messages" - android:layout_width="0px" - android:layout_height="wrap_content" - android:layout_weight="1" - android:text="@string/clear_messages" - android:onClick="onClearMessages" - /> - <Button android:id="@+id/close_port" - android:layout_width="0px" - android:layout_height="wrap_content" - android:layout_weight="1" - android:text="@string/close_port" - android:onClick="onClosePort" - /> - </LinearLayout> -</LinearLayout> diff --git a/media/tests/NativeMidiDemo/res/mipmap-hdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index cde69bcccec6..000000000000 --- a/media/tests/NativeMidiDemo/res/mipmap-hdpi/ic_launcher.png +++ /dev/null diff --git a/media/tests/NativeMidiDemo/res/mipmap-mdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index c133a0cbd379..000000000000 --- a/media/tests/NativeMidiDemo/res/mipmap-mdpi/ic_launcher.png +++ /dev/null diff --git a/media/tests/NativeMidiDemo/res/mipmap-xhdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index bfa42f0e7b91..000000000000 --- a/media/tests/NativeMidiDemo/res/mipmap-xhdpi/ic_launcher.png +++ /dev/null diff --git a/media/tests/NativeMidiDemo/res/mipmap-xxhdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 324e72cdd748..000000000000 --- a/media/tests/NativeMidiDemo/res/mipmap-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/media/tests/NativeMidiDemo/res/values/strings.xml b/media/tests/NativeMidiDemo/res/values/strings.xml deleted file mode 100644 index 5b69b52a5836..000000000000 --- a/media/tests/NativeMidiDemo/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="app_name">NativeMidiDemo</string> - <string name="clear_messages">Clear Messages</string> - <string name="close_port">Close Port</string> -</resources> |