Java Delphi/Android在哪里搜索本机语言库?
我想将MIDI功能添加到Delphi Android应用程序中。MIDI可通过SoniVox库获得,SoniVox库可通过Android NDK访问。可以找到此驱动程序的示例。驱动程序是用C编写的,使用NDK可以创建一个本地语言库,该库可以通过System.loadLibrary调用访问Java Delphi/Android在哪里搜索本机语言库?,java,delphi,android-ndk,java-native-interface,Java,Delphi,Android Ndk,Java Native Interface,我想将MIDI功能添加到Delphi Android应用程序中。MIDI可通过SoniVox库获得,SoniVox库可通过Android NDK访问。可以找到此驱动程序的示例。驱动程序是用C编写的,使用NDK可以创建一个本地语言库,该库可以通过System.loadLibrary调用访问 // MidiDriver - An Android Midi Driver. // Copyright (C) 2013 Bill Farmer // Bill Farmer w
// MidiDriver - An Android Midi Driver.
// Copyright (C) 2013 Bill Farmer
// Bill Farmer william j farmer [at] yahoo [dot] co [dot] uk.
#include <jni.h>
// for EAS midi
#include "eas.h"
#include "eas_reverb.h"
// determines how many EAS buffers to fill a host buffer
#define NUM_BUFFERS 4
// EAS data
static EAS_DATA_HANDLE pEASData;
const S_EAS_LIB_CONFIG *pLibConfig;
static EAS_PCM *buffer;
static EAS_I32 bufferSize;
static EAS_HANDLE midiHandle;
// init EAS midi
jint
Java_org_drivers_midioutput_MidiDriver_init(JNIEnv *env,
jobject clazz)
{
EAS_RESULT result;
// get the library configuration
pLibConfig = EAS_Config();
if (pLibConfig == NULL || pLibConfig->libVersion != LIB_VERSION)
return 0;
// calculate buffer size
bufferSize = pLibConfig->mixBufferSize * pLibConfig->numChannels *
NUM_BUFFERS;
// init library
if ((result = EAS_Init(&pEASData)) != EAS_SUCCESS)
return 0;
// select reverb preset and enable
EAS_SetParameter(pEASData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET,
EAS_PARAM_REVERB_CHAMBER);
EAS_SetParameter(pEASData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS,
EAS_FALSE);
// open midi stream
if (result = EAS_OpenMIDIStream(pEASData, &midiHandle, NULL) !=
EAS_SUCCESS)
{
EAS_Shutdown(pEASData);
return 0;
}
return bufferSize;
}
// midi config
jintArray
Java_org_drivers_midioutput_MidiDriver_config(JNIEnv *env,
jobject clazz)
{
jboolean isCopy;
if (pLibConfig == NULL)
return NULL;
jintArray configArray = (*env)->NewIntArray(env, 4);
jint *config = (*env)->GetIntArrayElements(env, configArray, &isCopy);
config[0] = pLibConfig->maxVoices;
config[1] = pLibConfig->numChannels;
config[2] = pLibConfig->sampleRate;
config[3] = pLibConfig->mixBufferSize;
(*env)->ReleaseIntArrayElements(env, configArray, config, 0);
return configArray;
}
// midi render
jint
Java_org_drivers_midioutput_MidiDriver_render(JNIEnv *env,
jobject clazz,
jshortArray shortArray)
{
jboolean isCopy;
EAS_RESULT result;
EAS_I32 numGenerated;
EAS_I32 count;
jsize size;
// jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy)
// void ReleaseByteArrayElements(jbyteArray array, jbyte* elems,
// void* GetPrimitiveArrayCritical(JNIEnv*, jarray, jboolean*);
// void ReleasePrimitiveArrayCritical(JNIEnv*, jarray, void*, jint);
if (pEASData == NULL)
return 0;
buffer =
(EAS_PCM *)(*env)->GetShortArrayElements(env, shortArray, &isCopy);
size = (*env)->GetArrayLength(env, shortArray);
count = 0;
while (count < size)
{
result = EAS_Render(pEASData, buffer + count,
pLibConfig->mixBufferSize, &numGenerated);
if (result != EAS_SUCCESS)
break;
count += numGenerated * pLibConfig->numChannels;
}
(*env)->ReleaseShortArrayElements(env, shortArray, buffer, 0);
return count;
}
// midi write
jboolean
Java_org_drivers_midioutput_MidiDriver_write(JNIEnv *env,
jobject clazz,
jbyteArray byteArray)
{
jboolean isCopy;
EAS_RESULT result;
jint length;
EAS_U8 *buf;
if (pEASData == NULL || midiHandle == NULL)
return JNI_FALSE;
buf = (EAS_U8 *)(*env)->GetByteArrayElements(env, byteArray, &isCopy);
length = (*env)->GetArrayLength(env, byteArray);
result = EAS_WriteMIDIStream(pEASData, midiHandle, buf, length);
(*env)->ReleaseByteArrayElements(env, byteArray, buf, 0);
if (result != EAS_SUCCESS)
return JNI_FALSE;
return JNI_TRUE;
}
// shutdown EAS midi
jboolean
Java_org_drivers_midioutput_MidiDriver_shutdown(JNIEnv *env,
jobject clazz)
{
EAS_RESULT result;
if (pEASData == NULL || midiHandle == NULL)
return JNI_FALSE;
if ((result = EAS_CloseMIDIStream(pEASData, midiHandle)) != EAS_SUCCESS)
{
EAS_Shutdown(pEASData);
return JNI_FALSE;
}
if ((result = EAS_Shutdown(pEASData)) != EAS_SUCCESS)
return JNI_FALSE;
return JNI_TRUE;
}
在上面的示例中,putShort从MidiDriver调用函数write,MidiDriver是本机库中定义的函数。这一切在Java中都很好,但在Dellphi中,正如您可能已经猜到的那样,实践有点困难。要更详细地显示调用链,我需要在Delphi中使用整个装置,请参见下图
在libsonivox
(可在/system/lib
中找到)中可以找到函数EAS\u writemiDistrieam
,它是从libmidi中的函数write
调用的。因此(在任何地方都可以找到,但在/system/lib
和/vendor/lib
中也可以找到),它在MIDI_Output.apk的MIDI_Output.java
中声明,从MIDI_Output.java
调用它,它创建一个新的MIDI驱动程序
,并引用同一个包的MIDI_driver.write(…)
函数putShort
。最后,应该在Delphi中调用putShort
,但它永远不会到达那里
当MIDI\u输出
创建尝试加载MIDI
库的新MIDI驱动程序
时,它会中止。程序无法加载库“midi
”。我运行了adb-dlogcat
来查看发生了什么,输出如下所示。android屏幕上显示的错误消息将突出显示
D/dalvikvm( 5251): DexOpt: --- BEGIN 'MIDI_Output.apk' (bootstrap=0) ---
D/dalvikvm( 5320): DexOpt: load 50ms, verify+opt 174ms, 1050124 bytes
D/dalvikvm( 5251): DexOpt: --- END 'MIDI_Output.apk' (success) ---
D/dalvikvm( 5251): DEX prep '/storage/emulated/legacy/Data/d/MIDI_Output.apk': u
nzip in 14ms, rewrite 401ms
W/dalvikvm( 5251): dvmFindClassByName rejecting 'org.drivers.midioutput/MIDI_Out
put'
D/midi ( 5251): >> Before initializing MIDI driver version 1
W/dalvikvm( 5251): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initi
alizing Lorg/drivers/midioutput/MidiDriver;
W/System.err( 5251): java.lang.UnsatisfiedLinkError: Couldn't load midi from loa
der dalvik.system.DexClassLoader[DexPathList[[zip file "/storage/sdcard0/Data/d/
MIDI_Output.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]]: findLib
rary returned null
W/System.err( 5251): at java.lang.Runtime.loadLibrary(Runtime.java:358)
W/System.err( 5251): at java.lang.System.loadLibrary(System.java:526)
W/System.err( 5251): at org.drivers.midioutput.MidiDriver.<clinit>(MidiDriver
.java:177)
W/System.err( 5251): at org.drivers.midioutput.MIDI_Output.<init>(MIDI_Output
.java:22)
W/System.err( 5251): at java.lang.Class.newInstanceImpl(Native Method)
W/System.err( 5251): at java.lang.Class.newInstance(Class.java:1208)
W/System.err( 5251): at dalvik.system.NativeStart.run(Native Method)
D/dalvikvm( 5251): GC_FOR_ALLOC freed 795K, 9% free 9091K/9920K, paused 13ms,
total 13ms
D/dalvikvm( 5251): GC_CONCURRENT freed 9K, 4% free 9532K/9920K, paused
2ms+2ms, total 22ms
我的问题是:DalvikVM在上面描述的设置中(Delphi通过JNI调用Java类,Java通过NDK调用C库)在哪个目录中查找本机库
第二个问题:这到底是一个图书馆搜索路径问题吗?也许Delphi无法通过JNI调用NDK
我尽量简短。如果有人认为我应该添加更多代码,请告诉我。非常感谢您的帮助。Delphi使用的NDK库应该放在\android-NDK-r8e\platforms\android-14\arch arm\usr\lib
中。多亏了Arioch和Chris的建议,我才意识到这一点,他们建议我直接使用NDK。Source\rtl\android
目录中的大多数android文件都包含包含以下定义的文件Androidapi.inc
const
AndroidLib = '/usr/lib/libandroid.so';
AndroidJniGraphicsLib = '/usr/lib/libjnigraphics.so';
AndroidEglLib = '/usr/lib/libEGL.so';
AndroidGlesLib = '/usr/lib/libGLESv1_CM.so';
AndroidGles2Lib = '/usr/lib/libGLESv2.so';
AndroidLogLib = '/usr/lib/liblog.so';
AndroidOpenSlesLib = '/usr/lib/libOpenSLES.so';
Nexus-7上不存在目录/usr/lib
,但我在上面提到的路径中找到了它,其中包含const
部分中声明的所有文件。将libmidi.so
复制到此目录解决了问题。我现在有一个不太小的问题,就是听不到声音。我现在将尝试解决这个问题,并尝试直接给NDK打电话。为什么要进行如此复杂的循环<代码>NDK(Delphi)=>SDK(Java)=>NDK(C++SoniVox)
?为什么不保持原生和直接:<代码> NDK(Delphi=C++ + SuniVox)< /C>?我非常愿意,但我不知道如何。找不到有关如何从Delphi调用NDK的任何参考。因此绕道。无法从加载midi-看起来您应该在静态{System.loadLibrary(“midi”);}
@Arioch'the中加载“libmidi.so”而不是“midi”-AndroidAPI.*单元用于Java API层;与此相反,本机代码(本质上是Linux)API头的翻译在Posix.*单元范围下,就像在OS X和iOS中一样。尽管如此,我同意你的主要观点——调用本机代码。因此,通过Java包装似乎有点奇怪。Arnold-您是否尝试过直接声明C导入?语法与声明Windows DLL导入单元完全相同。@Arnold-TBH,整个事情对我来说仍然有点模糊。我说libmidi是对的吗?SoniVoc库本身不是,而是另一个使用它的库?如果是这样的话,那么如果您想在Delphi中重新创建这个库,我认为目前这是不可能的,因为XE5似乎不支持创建独立的.so文件。另一方面,如果您只想使用libmidi.so,那么它是Java还是本机代码库?如果是前者,则可能需要处理线程复杂性(FMX应用程序不是一个而是两个主线程,本机代码线程和Java线程)。也许您可以使用符号链接而不是复制。因此,您将有一个单一的加载文件没有版本不匹配后,未来updates@Arioch-你说得有道理。首先,在我开始做类似的事情之前,我想了解整个过程。在这个NDK过程中,我仍然需要了解很多事情,我唯一想要的就是编程MIDI:-)也许存在本地Delphi MIDI播放库?不,Delphi和MIDI从来没有任何意义。当Delphi只是windows时,这并不是一个问题:windows API还不错。然而,对于Android来说,没有MIDI库本身就无法编程MIDI。甚至mediaplayer也不播放MIDI!SoniVox界面只是一项私人计划,与谷歌无关。顺便说一句:有一个链接到Delphi的外部库,但在Android上启动时崩溃了。在我发布下一个问题之前,我必须了解更多;-)明天做,现在太累了。
D/dalvikvm( 5251): DexOpt: --- BEGIN 'MIDI_Output.apk' (bootstrap=0) ---
D/dalvikvm( 5320): DexOpt: load 50ms, verify+opt 174ms, 1050124 bytes
D/dalvikvm( 5251): DexOpt: --- END 'MIDI_Output.apk' (success) ---
D/dalvikvm( 5251): DEX prep '/storage/emulated/legacy/Data/d/MIDI_Output.apk': u
nzip in 14ms, rewrite 401ms
W/dalvikvm( 5251): dvmFindClassByName rejecting 'org.drivers.midioutput/MIDI_Out
put'
D/midi ( 5251): >> Before initializing MIDI driver version 1
W/dalvikvm( 5251): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initi
alizing Lorg/drivers/midioutput/MidiDriver;
W/System.err( 5251): java.lang.UnsatisfiedLinkError: Couldn't load midi from loa
der dalvik.system.DexClassLoader[DexPathList[[zip file "/storage/sdcard0/Data/d/
MIDI_Output.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]]: findLib
rary returned null
W/System.err( 5251): at java.lang.Runtime.loadLibrary(Runtime.java:358)
W/System.err( 5251): at java.lang.System.loadLibrary(System.java:526)
W/System.err( 5251): at org.drivers.midioutput.MidiDriver.<clinit>(MidiDriver
.java:177)
W/System.err( 5251): at org.drivers.midioutput.MIDI_Output.<init>(MIDI_Output
.java:22)
W/System.err( 5251): at java.lang.Class.newInstanceImpl(Native Method)
W/System.err( 5251): at java.lang.Class.newInstance(Class.java:1208)
W/System.err( 5251): at dalvik.system.NativeStart.run(Native Method)
D/dalvikvm( 5251): GC_FOR_ALLOC freed 795K, 9% free 9091K/9920K, paused 13ms,
total 13ms
D/dalvikvm( 5251): GC_CONCURRENT freed 9K, 4% free 9532K/9920K, paused
2ms+2ms, total 22ms
package org.drivers.midioutput;
import android.util.Log;
public class class_test
{
public int test_int (int n)
{
int sq = n * n;
String mess = "*** test_int computes " + String.valueOf (sq);
Log.d ("midi", mess);
return n * n;
}
}
const
AndroidLib = '/usr/lib/libandroid.so';
AndroidJniGraphicsLib = '/usr/lib/libjnigraphics.so';
AndroidEglLib = '/usr/lib/libEGL.so';
AndroidGlesLib = '/usr/lib/libGLESv1_CM.so';
AndroidGles2Lib = '/usr/lib/libGLESv2.so';
AndroidLogLib = '/usr/lib/liblog.so';
AndroidOpenSlesLib = '/usr/lib/libOpenSLES.so';