billthefarmer / mididriver

Android midi driver using Sonivox EAS library
176 stars 52 forks source link

Using the MIDI driver from C/C++ codes #30

Closed kickmyassp closed 5 years ago

kickmyassp commented 6 years ago

Thanks for the java interface for SONIVOX midi synthesizer (That is what this is, I think) Before I found it, porting fluidsyth seemed the only way to go, but with a lot of works.

My MIDI notes come from C codes. I tried to play the notes using the driver through JNI but it failed due to a thread issue. An alternative would be to bring the MIDI notes over via JNI and play them in java, but it means no real-time playback.

Is it possible to use driver directly in native C/C++ instead? I noticed "Native methods" were mentioned in doc but without enough details.

Apisov commented 6 years ago

In case if the problem still exists for you here what can you do:

Change all to a specific ABI for example armeabi-v7a in this file https://github.com/billthefarmer/mididriver/blob/def9e835ae11f341ab20f010315ddc727fe79328/library/src/main/jni/Application.mk#L2

I guess it's somehow related to the latest releases of Android Studio, if you need to have more than one ABI try another way of declaration multiply ABIs described here:

billthefarmer commented 6 years ago

The reference to native interface means a native interface visible and callable from Java. If you just want to play notes you can use the two functions

jboolean Java_org_billthefarmer_mididriver_MidiDriver_init(JNIEnv *env, jobject obj)
jboolean Java_org_billthefarmer_mididriver_MidiDriver_shutdown(JNIEnv *env, jobject obj)

directly from C/C++ as the JNI args are ignored and the result is boolean, or you can copy the code, remove the JNI stuff and just call it midi_init() and midi_shutdown(); The other function you need is

jboolean Java_org_billthefarmer_mididriver_MidiDriver_write(JNIEnv *env, jobject obj, jbyteArray byteArray)

but that's not callable from C/C++ because of the JNI argument, so you will need to copy it and remove the JNI code and call it midi_write(). There is also

jbooleanJava_org_billthefarmer_mididriver_MidiDriver_setVolume(JNIEnv *env, jobject obj, jint volume)

You can call that direct by passing two nulls or zeros for the first two arguments, which are ignored, or copy the code as above.

kickmyassp commented 6 years ago

Thanks to both you gentlemen,

I appreciate billthefarmer's suggestion on using C/C++ functions directly. It seems to me that I still need to make some minor changes to the source and I want to be able to build the package before I make any changes and that's where I am stuck now.

Apisov's suggestion on APP_ABI in Application.mk did not help. I still get

Android NDK: INTERNAL ERROR: The armeabi ABI should have exactly one architecture definitions. Found: ''

I tried a few other things that seem to avoid the above problem but create new problems.

billthefarmer commented 6 years ago

I am somewhat puzzled as to why you think you need to build the library yourself. You are obviously using Android Studio, so you can add the library as a dependency as per

dependencies {
  compile 'com.github.billthefarmer:mididriver:v1.14'
}

And not use the Java part. Google are constantly moving the goalposts with their SDKs, once I've got something working nicely I tend to ignore the updates because, as you have found, it breaks things. Rebuilding this library with a later version of the tools won't make it any better, you will probably end up with an identical result.

kickmyassp commented 6 years ago

It's probably just my ignorance. I wasn't sure what functions in the library are actually accessible to my C/C++ codes, other than those few java methods mentioned in the doc. For instance, are all the functions defined in eas_public.c are accessible? If that's the case, I agree I don't need to build the library. Instead I just copy the header file over and build my version of the interface.

billthefarmer commented 6 years ago

Because the Sonivox library is written in C, all the functions are accessible. That doesn't mean it's a good idea to use them without setting up all the infrastructure first. I suggest you read the docs here: https://github.com/aosp-mirror/platform_external_sonivox/tree/master/docs. Particularly https://github.com/aosp-mirror/platform_external_sonivox/blob/master/docs/EASLibrary3_5.pdf. And the Android docs for OpenSLES https://developer.android.com/ndk/guides/audio/opensl/opensl-for-android.

billthefarmer commented 6 years ago

I have added a C/C++ native interface in version 1.15...

#include "midi.h"
    jboolean midi_init()  // Return true on success, or false on failure.
    jboolean midi_write(EAS_U8 *bytes, jint length)
                                 // Writes midi data to the Sonivox
                                 // synthesizer. The length of the array
                                 // should be the exact length of the
                                 // message or messages. Returns true
                                 // on success, false on
                                 // failure.
    jboolean midi_setVolume(jint volume)
                                  // Set master volume for EAS
                                  // synthesizer (between 0 and 100).
                                  // Returns true on success, false on
                                  // failure.
    jboolean midi_shutdown() // Shut down the synthesizer. Returns true on
                             // success, false on failure.
kickmyassp commented 6 years ago

I hope you would soon post the aar file for this version as well.

I tried to make the native interface myself with 1.14 library but failed since the compiler says all those functions defined in eas_public.c (EAS_Config, EAS_Init, EAS_WriteMIDIStream,...) are undefined references. As of now, my app can use MIDIDriver java class but not C functions behind it.

So I am very happy to see you decided to do it with 1.15.

Apisov commented 6 years ago

If you still want to build the project I forgot to mention that you also probably needed to update Android Gradle plugin to 3.1.4

https://github.com/billthefarmer/mididriver/blob/61cd32e3406d011df7139b26b6e204845901aa38/build.gradle#L7

and Gradle version to 4.4

https://github.com/billthefarmer/mididriver/blob/61cd32e3406d011df7139b26b6e204845901aa38/gradle/wrapper/gradle-wrapper.properties#L6

change it here to gradle-4.4-all.zip

I had the same error as you are. After changing to these versions I was able to build the project and run it.

There is another important thing to check, the NDK version. I use the latest - 17.1.4828580

PS: I did change the compile SDK too but I doubt that it afects somehow this question.

kickmyassp commented 6 years ago

On 1.15.aar: I request you to create an app in which those new native C/C++ functions are actually used. My attempt failed. These functions remain undefined as clang++ command line does not link/load the AAR file. I guess that AAR is mainly for sharing java codes and clang doesn't know how to use it.

kickmyassp commented 6 years ago

On building the project: I followed Apisov's suggestions:

I get this error message:

Build command failed. Error while executing process /Users/macuser/Library/Android/sdk/ndk-bundle/ndk-build with arguments {NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=/Users/macuser/tmp/mididriver/library/src/main/jni/Android.mk NDK_APPLICATION_MK=/Users/macuser/tmp/mididriver/library/src/main/jni/Application.mk APP_ABI=armeabi-v7a NDK_ALL_ABIS=armeabi-v7a NDK_DEBUG=1 APP_PLATFORM=android-14 NDK_OUT=/Users/macuser/tmp/mididriver/library/build/intermediates/ndkBuild/debug/obj NDK_LIBS_OUT=/Users/macuser/tmp/mididriver/library/build/intermediates/ndkBuild/debug/lib /Users/macuser/tmp/mididriver/library/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libmidi.so} Android NDK: WARNING:/Users/macuser/tmp/mididriver/library/src/main/jni/Android.mk:sonivox: LOCAL_LDLIBS is always ignored for static libraries
[armeabi-v7a] Compile++ thumb: midi <= midi.cpp [armeabi-v7a] Compile arm : sonivox <= eas_chorus.c clang: error: unsupported argument '--defsym' to option 'Wa,' clang: error: unsupported argument 'SAMPLE_RATE_22050=1' to option 'Wa,' clang: error: unsupported argument '--defsym' to option 'Wa,' clang: error: unsupported argument 'STEREO_OUTPUT=1' to option 'Wa,' clang: error: unsupported argument '--defsym' to option 'Wa,' clang: error: unsupported argument 'FILTER_ENABLED=1' to option 'Wa,' clang: error: unsupported argument '--defsym' to option 'Wa,' clang: error: unsupported argument 'SAMPLES_8_BIT=1' to option 'Wa,' make: *** [/Users/macuser/tmp/mididriver/library/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/objs-debug/sonivox/lib_src/eas_chorus.o] Error 1

This seems to complain about Android.mk, in particular, these lines:

asm_flags := \
    -I $(LOCAL_PATH)/lib_src \
    --defsym SAMPLE_RATE_22050=1 \
    --defsym STEREO_OUTPUT=1 \
    --defsym FILTER_ENABLED=1 \
    --defsym SAMPLES_8_BIT=1

LOCAL_CFLAGS += \
    $(foreach f,$(asm_flags),-Wa,"$(f)")

This is the same place I got stuck once before and I am very lost at this point.

billthefarmer commented 6 years ago

I don't know the answer, I expect it's in the android docs somewhere, or try stackoverflow. In an app I build using the driver, the native midi library is in build/intermediates/exploded-aar/com.github.billthefarmer/mididriver/v1.14/jni/<arch>/libmidi.so. That could change with a different version of the gradle build tools. You will need the include files midi.h and eas.h.

You appear to attempting to rebuild the driver with different tools. It builds perfectly with the tools I am using, which are:

Gradle 2.14.1 Android gradle build tools 2.2.2 Android build tools 25.0.2 NDK android-ndk-r14b

I appreciate that these are now old, but I wrote it five years ago. If it ain't broke, don't fix it.

billthefarmer commented 6 years ago

Ok, here is a test project that does nothing, but includes a C++ file that references all the exported C/C++ functions in the midi library. https://github.com/billthefarmer/test. I have used the .aar file because the Jitpack build failed for the same reason as yours.

dependencies {
    compile 'org.billthefarmer.mididriver:MidiDriver-1.15@aar'
}

repositories{
    flatDir{
        dirs 'libs'
    }
}
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := midi
LOCAL_SRC_FILES := $(LOCAL_PATH)/../../../build/intermediates/exploded-aar/org.billthefarmer.mididriver/MidiDriver-1.15/jni/$(TARGET_ARCH_ABI)/libmidi.so

include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE    := test
LOCAL_SRC_FILES := test.c++
LOCAL_SHARED_LIBRARIES := midi
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

#include "midi.h"

void test()
{
    jboolean result;
    EAS_U8 midi[3];

    result = midi_init();
    result = midi_write(midi, sizeof(midi));
    result = midi_setVolume(99);
    result = midi_shutdown();
}
Compilation started at Thu Aug 16 17:53:48

gradle assembleRelease
Incremental java compilation is an incubating feature.
:app:preBuild UP-TO-DATE
:app:preReleaseBuild UP-TO-DATE
:app:checkReleaseManifest
:app:preDebugBuild UP-TO-DATE
:app:prepareOrgBillthefarmerMididriverMidiDriver115Library UP-TO-DATE
:app:prepareReleaseDependencies
:app:compileReleaseAidl UP-TO-DATE
:app:compileReleaseRenderscript UP-TO-DATE
:app:generateReleaseBuildConfig
:app:generateReleaseResValues UP-TO-DATE
:app:generateReleaseResources UP-TO-DATE
:app:mergeReleaseResources UP-TO-DATE
:app:processReleaseManifest UP-TO-DATE
:app:processReleaseResources UP-TO-DATE
:app:generateReleaseSources
:app:incrementalReleaseJavaCompilationSafeguard
:app:compileReleaseJavaWithJavac
:app:compileReleaseJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
:app:generateJsonModelRelease UP-TO-DATE
:app:externalNativeBuildRelease
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\mips64\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\mips\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\x86_64\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\x86\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\arm64-v8a\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\armeabi-v7a\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\armeabi\libtest.so
:app:compileReleaseSources
:app:lintVitalRelease
:app:mergeReleaseShaders UP-TO-DATE
:app:compileReleaseShaders UP-TO-DATE
:app:generateReleaseAssets UP-TO-DATE
:app:mergeReleaseAssets UP-TO-DATE
:app:transformClassesWithDexForRelease
:app:mergeReleaseJniLibFolders UP-TO-DATE
:app:transformNative_libsWithMergeJniLibsForRelease UP-TO-DATE
:app:transformNative_libsWithStripDebugSymbolForRelease UP-TO-DATE
:app:processReleaseJavaRes UP-TO-DATE
:app:transformResourcesWithMergeJavaResForRelease UP-TO-DATE
:app:packageRelease
:app:assembleRelease

BUILD SUCCESSFUL

Total time: 5.547 secs

Compilation finished at Thu Aug 16 17:53:55
billthefarmer commented 6 years ago

The reason for the NDK errors is that NDK r17b has withdrawn support for armeabi, mips and mips64. You need a ndk section in defaultConfig so it doesn't attempt to build them.


    defaultConfig {
        buildConfigField "long", "BUILT", System.currentTimeMillis() + "L"

        ndk {
            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
        }
    }
Compilation started at Fri Aug 17 09:03:01

gradle assembleRelease
Incremental java compilation is an incubating feature.
:app:preBuild UP-TO-DATE
:app:preReleaseBuild UP-TO-DATE
:app:checkReleaseManifest
:app:preDebugBuild UP-TO-DATE
:app:prepareOrgBillthefarmerMididriverMidiDriver115Library UP-TO-DATE
:app:prepareReleaseDependencies
:app:compileReleaseAidl UP-TO-DATE
:app:compileReleaseRenderscript UP-TO-DATE
:app:generateReleaseBuildConfig
:app:generateReleaseResValues UP-TO-DATE
:app:generateReleaseResources UP-TO-DATE
:app:mergeReleaseResources UP-TO-DATE
:app:processReleaseManifest UP-TO-DATE
:app:processReleaseResources UP-TO-DATE
:app:generateReleaseSources
:app:incrementalReleaseJavaCompilationSafeguard
:app:compileReleaseJavaWithJavac
:app:compileReleaseJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
:app:generateJsonModelRelease UP-TO-DATE
:app:externalNativeBuildRelease
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\arm64-v8a\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\x86\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\x86_64\libtest.so
  building D:\android\Test\app\build\intermediates\ndkBuild\release\obj\local\armeabi-v7a\libtest.so
:app:compileReleaseSources
:app:lintVitalRelease
:app:mergeReleaseShaders UP-TO-DATE
:app:compileReleaseShaders UP-TO-DATE
:app:generateReleaseAssets UP-TO-DATE
:app:mergeReleaseAssets UP-TO-DATE
:app:transformClassesWithDexForRelease
:app:mergeReleaseJniLibFolders UP-TO-DATE
:app:transformNative_libsWithMergeJniLibsForRelease UP-TO-DATE
:app:transformNative_libsWithStripDebugSymbolForRelease UP-TO-DATE
:app:processReleaseJavaRes UP-TO-DATE
:app:transformResourcesWithMergeJavaResForRelease UP-TO-DATE
:app:packageRelease
:app:assembleRelease

BUILD SUCCESSFUL

Total time: 4.0 secs

Compilation finished at Fri Aug 17 09:03:06
billthefarmer commented 6 years ago

The reason the mididriver won't build is the above, plus NDK r17b has withdrawn support for the GNU compiler, which must include the assembler. I checked the platform_external_sonivox buil;d file - https://github.com/aosp-mirror/platform_external_sonivox/blob/master/arm-wt-22k/Android.bp and they haven't ported the assembler files, so I'm embuggered unless I remove the arm assembler option. That's only the latest embuggerment, they've done it several times.

NDK r16b works fine.

kickmyassp commented 6 years ago

billthefarmer,

I finally have your 'test' project working on my side. Different versions of tools seem to have created some nasty problems. For benefit for others who might try this, here is a summary of the problems I encountered in trying to make 'test' project work.

Thank you for helping me out on this. (I am also a farmer and musician playing clarinet)

bneumann commented 6 years ago

Thanks for the native interface. Makes it usable through xamarin much easier. Good stuff!

Note: while playing around I found that it also compiles nicely with clang 5 on ARM and x86 (the ones I tried).