|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2015 - ROLI Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
- METHOD (getJuceAndroidMidiInputDevices, "getJuceAndroidMidiInputDevices", "()[Ljava/lang/String;") \
- METHOD (getJuceAndroidMidiOutputDevices, "getJuceAndroidMidiOutputDevices", "()[Ljava/lang/String;") \
- METHOD (openMidiInputPortWithJuceIndex, "openMidiInputPortWithJuceIndex", "(IJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
- METHOD (openMidiOutputPortWithJuceIndex, "openMidiOutputPortWithJuceIndex", "(I)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
- METHOD (getInputPortNameForJuceIndex, "getInputPortNameForJuceIndex", "(I)Ljava/lang/String;") \
- METHOD (getOutputPortNameForJuceIndex, "getOutputPortNameForJuceIndex", "(I)Ljava/lang/String;")
- DECLARE_JNI_CLASS (MidiDeviceManager, JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager")
- #undef JNI_CLASS_MEMBERS
-
- #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
- METHOD (start, "start", "()V" )\
- METHOD (stop, "stop", "()V") \
- METHOD (close, "close", "()V") \
- METHOD (sendMidi, "sendMidi", "([BII)V")
- DECLARE_JNI_CLASS (JuceMidiPort, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort")
- #undef JNI_CLASS_MEMBERS
-
-
- //==============================================================================
- class AndroidMidiInput
- {
- public:
- AndroidMidiInput (MidiInput* midiInput, int portIdx,
- juce::MidiInputCallback* midiInputCallback, jobject deviceManager)
- : juceMidiInput (midiInput),
- callback (midiInputCallback),
- midiConcatenator (2048),
- javaMidiDevice (getEnv()->CallObjectMethod (deviceManager,
- MidiDeviceManager.openMidiInputPortWithJuceIndex,
- (jint) portIdx,
- (jlong) this))
- {
- }
-
- ~AndroidMidiInput()
- {
- if (jobject d = javaMidiDevice.get())
- {
- getEnv()->CallVoidMethod (d, JuceMidiPort.close);
- javaMidiDevice.clear();
- }
- }
-
- bool isOpen() const noexcept
- {
- return javaMidiDevice != nullptr;
- }
-
- void start()
- {
- if (jobject d = javaMidiDevice.get())
- getEnv()->CallVoidMethod (d, JuceMidiPort.start);
- }
-
- void stop()
- {
- if (jobject d = javaMidiDevice.get())
- getEnv()->CallVoidMethod (d, JuceMidiPort.stop);
-
- callback = nullptr;
- }
-
- void receive (jbyteArray byteArray, jlong offset, jint len, jlong timestamp)
- {
- jassert (byteArray != nullptr);
- jbyte* data = getEnv()->GetByteArrayElements (byteArray, nullptr);
-
- HeapBlock<uint8> buffer (len);
- std::memcpy (buffer.getData(), data + offset, len);
-
- midiConcatenator.pushMidiData (buffer.getData(),
- len, static_cast<double> (timestamp) * 1.0e-9,
- juceMidiInput, *callback);
-
- getEnv()->ReleaseByteArrayElements (byteArray, data, 0);
- }
-
- private:
- MidiInput* juceMidiInput;
- MidiInputCallback* callback;
- GlobalRef javaMidiDevice;
- MidiDataConcatenator midiConcatenator;
- };
-
- //==============================================================================
- class AndroidMidiOutput
- {
- public:
- AndroidMidiOutput (jobject midiDevice)
- : javaMidiDevice (midiDevice)
- {
- }
-
- ~AndroidMidiOutput()
- {
- if (jobject d = javaMidiDevice.get())
- {
- getEnv()->CallVoidMethod (d, JuceMidiPort.close);
- javaMidiDevice.clear();
- }
- }
-
- void send (jbyteArray byteArray, jint offset, jint len)
- {
- if (jobject d = javaMidiDevice.get())
- getEnv()->CallVoidMethod (d,
- JuceMidiPort.sendMidi,
- byteArray, offset, len);
- }
-
- private:
- GlobalRef javaMidiDevice;
- };
-
- JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceMidiInputPort), handleReceive,
- void, (JNIEnv* env, jobject device, jlong host, jbyteArray byteArray,
- jint offset, jint count, jlong timestamp))
- {
- // Java may create a Midi thread which JUCE doesn't know about and this callback may be
- // received on this thread. Java will have already created a JNI Env for this new thread,
- // which we need to tell Juce about
- setEnv (env);
-
- reinterpret_cast<AndroidMidiInput*> (host)->receive (byteArray, offset, count, timestamp);
- }
-
- //==============================================================================
- class AndroidMidiDeviceManager
- {
- public:
- AndroidMidiDeviceManager()
- : deviceManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidMidiDeviceManager))
- {
- }
-
- String getInputPortNameForJuceIndex (int idx)
- {
- if (jobject dm = deviceManager.get())
- {
- LocalRef<jstring> string ((jstring) getEnv()->CallObjectMethod (dm, MidiDeviceManager.getInputPortNameForJuceIndex, idx));
- return juceString (string);
- }
-
- return String();
- }
-
- String getOutputPortNameForJuceIndex (int idx)
- {
- if (jobject dm = deviceManager.get())
- {
- LocalRef<jstring> string ((jstring) getEnv()->CallObjectMethod (dm, MidiDeviceManager.getOutputPortNameForJuceIndex, idx));
- return juceString (string);
- }
-
- return String();
- }
-
- StringArray getDevices (bool input)
- {
- if (jobject dm = deviceManager.get())
- {
- jobjectArray jDevices
- = (jobjectArray) getEnv()->CallObjectMethod (dm, input ? MidiDeviceManager.getJuceAndroidMidiInputDevices
- : MidiDeviceManager.getJuceAndroidMidiOutputDevices);
-
- // Create a local reference as converting this
- // to a JUCE string will call into JNI
- LocalRef<jobjectArray> devices (jDevices);
- return javaStringArrayToJuce (devices);
- }
-
- return StringArray();
- }
-
- AndroidMidiInput* openMidiInputPortWithIndex (int idx, MidiInput* juceMidiInput, juce::MidiInputCallback* callback)
- {
- if (jobject dm = deviceManager.get())
- {
- ScopedPointer<AndroidMidiInput> androidMidiInput (new AndroidMidiInput (juceMidiInput, idx, callback, dm));
-
- if (androidMidiInput->isOpen())
- return androidMidiInput.release();
- }
-
- return nullptr;
- }
-
- AndroidMidiOutput* openMidiOutputPortWithIndex (int idx)
- {
- if (jobject dm = deviceManager.get())
- if (jobject javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithJuceIndex, (jint) idx))
- return new AndroidMidiOutput (javaMidiPort);
-
- return nullptr;
- }
-
- private:
- static StringArray javaStringArrayToJuce (jobjectArray jStrings)
- {
- StringArray retval;
-
- JNIEnv* env = getEnv();
- const int count = env->GetArrayLength (jStrings);
-
- for (int i = 0; i < count; ++i)
- {
- LocalRef<jstring> string ((jstring) env->GetObjectArrayElement (jStrings, i));
- retval.add (juceString (string));
- }
-
- return retval;
- }
-
- GlobalRef deviceManager;
- };
-
- //==============================================================================
- StringArray MidiOutput::getDevices()
- {
- AndroidMidiDeviceManager manager;
- return manager.getDevices (false);
- }
-
- int MidiOutput::getDefaultDeviceIndex()
- {
- return 0;
- }
-
- MidiOutput* MidiOutput::openDevice (int index)
- {
- if (index < 0)
- return nullptr;
-
- AndroidMidiDeviceManager manager;
-
- String midiOutputName = manager.getOutputPortNameForJuceIndex (index);
-
- if (midiOutputName.isEmpty())
- {
- // you supplied an invalid device index!
- jassertfalse;
- return nullptr;
- }
-
- if (AndroidMidiOutput* midiOutput = manager.openMidiOutputPortWithIndex (index))
- {
- MidiOutput* retval = new MidiOutput (midiOutputName);
- retval->internal = midiOutput;
-
- return retval;
- }
-
- return nullptr;
- }
-
- MidiOutput::~MidiOutput()
- {
- stopBackgroundThread();
-
- delete reinterpret_cast<AndroidMidiOutput*> (internal);
- }
-
- void MidiOutput::sendMessageNow (const MidiMessage& message)
- {
- if (AndroidMidiOutput* androidMidi = reinterpret_cast<AndroidMidiOutput*>(internal))
- {
- JNIEnv* env = getEnv();
- const int messageSize = message.getRawDataSize();
-
- LocalRef<jbyteArray> messageContent = LocalRef<jbyteArray> (env->NewByteArray (messageSize));
- jbyteArray content = messageContent.get();
-
- jbyte* rawBytes = env->GetByteArrayElements (content, nullptr);
- std::memcpy (rawBytes, message.getRawData(), messageSize);
- env->ReleaseByteArrayElements (content, rawBytes, 0);
-
- androidMidi->send (content, (jint) 0, (jint) messageSize);
- }
- }
-
- //==============================================================================
- MidiInput::MidiInput (const String& nm) : name (nm)
- {
- }
-
- StringArray MidiInput::getDevices()
- {
- AndroidMidiDeviceManager manager;
- return manager.getDevices (true);
- }
-
- int MidiInput::getDefaultDeviceIndex()
- {
- return 0;
- }
-
- MidiInput* MidiInput::openDevice (int index, juce::MidiInputCallback* callback)
- {
- if (index < 0)
- return nullptr;
-
- AndroidMidiDeviceManager manager;
-
- String midiInputName = manager.getInputPortNameForJuceIndex (index);
-
- if (midiInputName.isEmpty())
- {
- // you supplied an invalid device index!
- jassertfalse;
- return nullptr;
- }
-
- ScopedPointer<MidiInput> midiInput (new MidiInput (midiInputName));
-
- midiInput->internal = manager.openMidiInputPortWithIndex (index, midiInput, callback);
-
- return midiInput->internal != nullptr ? midiInput.release()
- : nullptr;
- }
-
- void MidiInput::start()
- {
- if (AndroidMidiInput* mi = reinterpret_cast<AndroidMidiInput*> (internal))
- mi->start();
- }
-
- void MidiInput::stop()
- {
- if (AndroidMidiInput* mi = reinterpret_cast<AndroidMidiInput*> (internal))
- mi->stop();
- }
-
- MidiInput::~MidiInput()
- {
- delete reinterpret_cast<AndroidMidiInput*> (internal);
- }
|