实现MIDI与德尔福在Android [英] Implement MIDI with Delphi on Android

查看:536
本文介绍了实现MIDI与德尔福在Android的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我期待,因为一段时间的方式来在Delphi XE5与Android播放MIDI针对性。我的几个问题,之前与此相关的任务:-)。我已提交了两份要求Embarcadero公司:#119422 以MIDI支持添加到TMediaPlayer和#119423 添加一个MIDI框架Firemonkey,但这没有帮助。我已经成功地完成了最后。当我知道有一些更多的人谁正在寻找MIDI Android上我张贴与文档回答这个问题。

I am looking since some time for a way to play MIDI in Delphi XE5 with Android targeted. Several of my questions before were related to this "quest" :-). I have filed two requests to embarcadero: #119422 to add MIDI support to TMediaPlayer and #119423 to add a MIDI framework to Firemonkey, but that did not help. I have succeeded at last. As I know there are some more people who were looking for MIDI on Android I post this question with answer for documentation.

推荐答案

Android系统有一个内部的MIDI合成器。您可以通过Android NDK访问它。我曾在一个<一个描述这个href="http://www.musoft-builders.nl/music/accessing-midi-in-delphi-for-android/?$p$pview=true&$p$pview_id=375&$p$pview_nonce=8bb3f87e95"相对=nofollow>文章包含了一些下载。这个答案是本文的简短描述。什么,你会在这里看到的是一种概念证明。它将展示如何播放MIDI音符在Android系统上,但需要改进。提出改进建议,欢迎: - )

The Android system has an internal MIDI synthesizer. You can access it via the Android NDK. I have described this in an article containing some downloads. This answer is a short description of this article. What you'll see here is a Proof of Concept. It will show how to play MIDI notes on an Android system but needs improvement. Suggestions for improvement are welcome :-)

使用Eclipse中的接口与Java项目。我presume你有德尔福XE5的手机包,它给你已经安装了两件事情:在Android SDK和NDK。 不要重装这些通过下载从谷歌的完整的Andr​​oid SDK中。 下载并安装 Eclipse的Andr​​oid开发工具(ADT)插件,并按照安装指导。这使您可以使用已经安装了德尔福XE5了Android SDK / NDK环境(你会发现在Delphi中,选择的路径|工具| SDK管理器)。这样,Delphi和Eclipse将共享相同的SDK和NDK。

Use Eclipse to interface with the Java project. I presume you have Delphi XE5 with the Mobile pack, which gives you two things already installed: the Android SDK and NDK. Do not reinstall these by downloading the complete Android SDK from Google. Download and install the Eclipse Android Development Tools (ADT) plugin and follow the installation instructions. This allows you to use the Android SDK/NDK environment already installed by Delphi XE5 (you will find the paths in Delp Options | Tools | SDK Manager). In this way Delphi and Eclipse will share the same SDK and NDK.

我使用的威廉农民开发的 MIDI库。他也有充分的SoniVox文档可我无法得到其他地方。他的司机配备了一个完整的示例(Java)的计划。我创建了自己的项目,改变了包名org.drivers.midioutput,因此所有功能都通过Java_org_drivers_midioutput_MidiDriver_ pfixed $ P $(见下文code)。

I used the MIDI library developed by Willam Farmer. He also has the full SoniVox documentation available which I couldn't get elsewhere. His driver comes with a full example (Java) program. I created my own project with and changed the package name to org.drivers.midioutput, so all functions are prefixed by Java_org_drivers_midioutput_MidiDriver_ (see code below).

当你想编译midi.c强行打开命令窗口,并调用NDK,建立在项目目录。某些错误消息都OK。在MIPS和x86库并没有建在我的情况。

When you wish to compile the midi.c jus open a command window, and call ndk-build in the project directory. Some error messages are ok. The mips and x86 libraries were not built in my case.

有一点,虽然你应该知道的:在路径NDK不能包含空格。当你让德尔福安装程序进行安装德尔福也必将是它的空间​​:子目录的RAD Studio在德尔福安装SDK和NDK那个可怕的长文件名。为了解决此问题,在驱动器C的空目录:,把它叫做C:\ NDK。使用 MKLINK 到这个目录链接NDK的目录。这只能从提升的命令提示符下执行的,当你这样做,你就会失去你的网络连接。这个链接是持久的,因此只需关闭命令提示符窗口,打开另一个,unelevated一个,都应该现在的工作。现在,您可以真正使用NDK建造。

There is one point though you should be aware of: the path to the ndk may not contain spaces. As you let the Delphi installer install Delphi there is bound to be a space in it: the subdirectory Rad Studio in that terrible long file name where Delphi installs the SDK and NDK. In order to work around this problem, create an empty directory on drive C:, call it C:\ndk. Use MKLINK to link this directory to the ndk directory. This can only be done from an elevated command prompt and as you do so you'll lose your network connections. The link is persistent so just close the command prompt and open another, unelevated one, and all should work now. Now you can really use ndk-build.

midi.c - NDK的接口与SoniVox

midi.c - the NDK interface with the SoniVox

////////////////////////////////////////////////////////////////////////////////
//
//  MidiDriver - An Android Midi Driver.
//
//  Copyright (C) 2013  Bill Farmer
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//  Bill Farmer  william j farmer [at] yahoo [dot] co [dot] uk.
//
///////////////////////////////////////////////////////////////////////////////

// Some slight modifications by Arnold Reinders. Added a test function and changed
// the package to org.drivers.midioutput. The original copyright still applies 

#include 

// 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;

// This function is added to test whether the functionality of this NDK code can be accesses
// without needing to access the MIDI system. Added for testing purposes
jint
Java_org_drivers_midioutput_MidiDriver_version (JNIEnv *env, jobject clazz)
{
   return 3;
}

// 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;
}

在该库由内置NDK,建造这将preFIX与lib中的编译库和。所以更换扩展。所以midi.c将编译到libmidi.so。编译库添加到下载,让你不必编译midi.c。

When the library is built by ndk-build this will prefix the compiled library with lib and replace the extension by .so. So midi.c will compile to libmidi.so. Compiled libraries are added to the download, so you needn't compile midi.c.

MidiDriver.Java声明了一个接口,audioTrack和一个线程来处理这一切。我没有不厌其烦地寻找究竟如何工作的。因为我不知道如何处理接口,并在这样的德尔福我创建一个Java包装MidiDriver:类MIDI_Output。此类用于与德尔福连接。

MidiDriver.Java declares an interface, an audioTrack and a thread to handle all these. I haven't taken the trouble to find how exactly this works. Because I didn't know how to handle an interface and such in Delphi I created a Java wrapper for MidiDriver: class MIDI_Output. This class is used to interface with Delphi.

类MidiDriver是爪哇和C-函数调用SoniVox功能之间的界面。类MIDI_Output是Java和德尔福之间的接口。 MIDI_Output创建MidiDriver的一个实例。

Class MidiDriver is the interface between Java and the C-functions that call SoniVox functions. Class MIDI_Output is the interface between Java and Delphi. MIDI_Output creates an instance of MidiDriver.

类MidiDriver - 与NDK接口

Class MidiDriver - the interface with the NDK

////////////////////////////////////////////////////////////////////////////////
//
//  MidiDriver - An Android Midi Driver.
//
//  Copyright (C) 2013  Bill Farmer
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//  Bill Farmer  william j farmer [at] yahoo [dot] co [dot] uk.
//
///////////////////////////////////////////////////////////////////////////////

package  org.drivers.midioutput;

import java.io.File;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;

// MidiDriver

public class MidiDriver implements Runnable
{
    private static final int SAMPLE_RATE = 22050;
    private static final int BUFFER_SIZE = 4096;

    private Thread thread;
    private AudioTrack audioTrack;

    private OnMidiStartListener listener;

    private short buffer[];

    // Constructor

    public MidiDriver ()
    {
       Log.d ("midi", "   ***   MidiDriver started");
    }

    public void start ()
    {
        // Start the thread
       thread = new Thread (this, "MidiDriver");
       thread.start ();
    } // start //

    @Override
    public void run ()
    {
        processMidi ();
    } // run //

    public void stop ()
    {
      Thread t = thread;
      thread = null;

      // Wait for the thread to exit

      while (t != null && t.isAlive ())
          Thread.yield ();
    } // stop //

    // Process MidiDriver

    private void processMidi ()
    {
      int status = 0;
      int size = 0;

      // Init midi

      Log.d ("midi", "   ***   processMIDI");
      if ((size = init()) == 0)
          return;

      buffer = new short [size];

      // Create audio track

      audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE,
                            AudioFormat.CHANNEL_OUT_STEREO,
                            AudioFormat.ENCODING_PCM_16BIT,
                            BUFFER_SIZE, AudioTrack.MODE_STREAM);
      if (audioTrack == null)
      {
          shutdown ();
          return;
      } // if

      // Call listener

      if (listener != null)
          listener.onMidiStart();

      // Play track

      audioTrack.play();

      // Keep running until stopped

      while (thread != null)
      {
          // Render the audio

          if (render (buffer) == 0)
             break;

          // Write audio to audiotrack

          status = audioTrack.write (buffer, 0, buffer.length);

          if (status < 0)              break;       } // while              // Render and write the last bit of audio              if (status > 0)
          if (render(buffer) > 0)
             audioTrack.write(buffer, 0, buffer.length);

      // Shut down audio

      shutdown();
      audioTrack.release();
    } // processMidi //

    public void setOnMidiStartListener (OnMidiStartListener l)
    {
       listener = l;
    } // setOnMidiStartListener //

    public static void load_lib (String libName) 
    {
       File file = new File (libName);

       if (file.exists ()) 
       {
           System.load (libName);
       } else 
       {
           System.loadLibrary (libName);
       }
   }    // Listener interface

    public interface OnMidiStartListener
    {
       public abstract void onMidiStart ();
    } // OnMidiStartListener //

    // Native midi methods

    public  native int     version ();
    private native int     init ();
    public  native int []  config ();
    private native int     render (short a []);
    public  native boolean write (byte a []);
    private native boolean shutdown ();

    // Load midi library

    static
    {
       System.loadLibrary ("midi");
    }
}

类MIDI_Output - 类MidiDriver提供包装

Class MIDI_Output - providing a wrap for class MidiDriver

package org.drivers.midioutput;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;

import org.drivers.midioutput.MidiDriver.OnMidiStartListener;

import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.os.Environment;
import android.util.Log;

public class MIDI_Output implements OnMidiStartListener
{
   protected MidiDriver midi_driver;
   protected MediaPlayer media_player;

   public MIDI_Output ()
   {
      // Create midi driver
      midi_driver = new MidiDriver();

      Log.d ("midi", "   ***   midi_driver opened with version "  +
                     String.valueOf (midi_driver.version ()));

// Set onmidistart listener to this class

      if (midi_driver != null)
          midi_driver.setOnMidiStartListener (this);
   } // MIDI_Output () //

   public int test_int (int n)
   {
      int sq = n * n;

//      Log.d ("midi", "   ***   test_int computes " + String.valueOf (sq));
      return n * n;
   }

   public void start ()
   {
      if (midi_driver != null)
      {
         midi_driver.start ();
         Log.d ("midi", "   ***   midi_driver.start ()");
      }
   } // start //

   public void stop ()
   {
      if (midi_driver != null)
      {
         midi_driver.stop ();
         Log.d ("midi", "   ***   midi_driver.stop ()");
      }

      stopSong ();
   } // stop //

   // Listener for sending initial midi messages when the Sonivox
   // synthesizer has been started, such as program change. Runs on
   // the MidiDriver thread, so should only be used for sending midi
   // messages.

   @Override
   public void onMidiStart()
   {
      Log.d ("midi", "   ***   onSMidiStart");
  // TODO
   }

   // Sends a midi message

   protected void putShort (int m, int n, int v)
   {
      if (midi_driver != null)
      {
        byte msg [] = new byte [3];

        msg [0] = (byte) m;
        msg [1] = (byte) n;
        msg [2] = (byte) v;

        Log.d ("midi", "   ***   putShort (" + String.valueOf (m) + ", " + String.valueOf (n) + ", " + String.valueOf (v) + ")");

        midi_driver.write (msg);
      } // if
   } // putShort //

   public boolean isPlayingSong ()
   {
      return media_player != null;
   } // isPlayingSong //

   public void playSong (String audioFilename)
   {
      String audioPath;

      try 
      {
         FileDescriptor fd = null;
         audioFilename = "/Data/d/song.mid";

         File baseDir = Environment.getExternalStorageDirectory ();
         audioPath = baseDir.getAbsolutePath () + audioFilename;

         Log.d ("midi", "   ***   Look for file: " + audioPath);

         FileInputStream fis = new FileInputStream (audioPath);
         fd = fis.getFD ();

         if (fd != null) 
         {
             Log.d ("midi", "   ***   Found file, trying to play: " + audioPath);
             MediaPlayer mediaPlayer = new MediaPlayer ();
             mediaPlayer.setDataSource (fd);
             mediaPlayer.prepare ();
             mediaPlayer.start ();
         }
     } catch (Exception e) 
     {
        Log.d ("midi", "   ***   Exception while trying to play file: " + e.getMessage ());
     }      
   }

   public void stopSong ()
   {
      if (media_player != null)
      {
         media_player.stop ();
         media_player.release ();
         media_player = null;
      } // if
   } // stopSong //
} // Class: MIDI_Output //

从MidiDriver和MIDI_Output创建一个Eclipse Android项目,一个MainActivity添加和运行。省去了很多的错误后,我得到它运行起来。一个有用的工具是Android调试器(ADB)。打开命令窗口并运行的adb -d logcat的。我已经加了很多log.d的(MIDI, * 的消息),以看到事情发生了错误,在code语句。删除他们,如果你不喜欢他们,但如果你知道到Android(我仍是在很大的程度),是看在你的应用程序会发生什么的有效途径。日志可以作为WEL在Delphi中,看到了德尔福源。

From MidiDriver and MIDI_Output an Eclipse Android project was created, a MainActivity added and run. After eliminating a lot of bugs I got it up and running. A useful tool is the android debugger (adb). Open a command windows and run adb -d logcat. I have added a lot of log.d ('midi", " * message') statements in the code in order to see where things went wrong. Remove them if you don't like them, but if you are unknown to Android (which I still am to a great extent) it is a useful way to see what happens in your application. Log works as wel in Delp see the Delphi Sources.

在程序编译好,你在你的项目\ bin目录下有一个MIDI_Output.apk包。这个软件包将被用于由德尔福运行Java的方法。

When the program compiles well you have a MIDI_Output.apk package in your project\bin directory. This package will be used by Delphi to run the Java methods.

Java可以从德尔福使用JNI来访问。一个动手的教程可以在 RedTitan 的网站上找到。本教程的想法在课堂TMIDI_Output_Device实施的。

Java can be accessed from Delphi by using JNI. A hands-on tutorial can be found on the site of RedTitan. The ideas of this tutorial were implemented in class TMIDI_Output_Device.

正如你可能会看到一个字符串常量test_apk_fn定义的路径MIDI_Output.apk Android包。此字符串提供JNI与将Java库可以找到的名称。该字符串javaClassName提供了Java接口所必需的包名。有了这些字符串德尔福JNI是能够找到所请求的类。

As you may see a constant string test_apk_fn is defined with the path to the MIDI_Output.apk Android package. This string provides JNI with the name where the Java library can be found. The string javaClassName provides the package name necessary to interface with Java. With these string the Delphi JNI is able to find the requested classes.

类TMIDI_Output_Device - 为Java类MIDI_Output提供一个Delphi包装

Class TMIDI_Output_Device - providing a Delphi wrap for Java class MIDI_Output

unit MIDI_Output_Device;

interface

uses
   System.SysUtils,
   FMX.Types,
   Androidapi.JNIBridge,
   Androidapi.JNI.JavaTypes,
   Androidapi.Jni,
   Androidapi.JNI.Dalvik,
   Androidapi.JNI.GraphicsContentViewText;

const
   test_apk_fn  = '/storage/sdcard0/Data/d/MIDI_Output.apk';

type
   TMIDI_Output_Device = class (TObject)
   private
      JavaEnv: PJNIEnv;
      context: JContext;
      CL: JDexClassLoader;
      JavaObject: JObject;
      JavaObjectID: JNIObject;
      jTempClass: Jlang_Class;
      jTemp: JObject;
      oTemp: TObject;
      jLocalInterface: ILocalObject;
      optimizedpath_jfile: JFile;
      dexpath_jstring, optimizedpath_jstring: JString;
      fun_version: JNIMethodID;
      fun_start: JNIMethodID;
      fun_put_short: JNIMethodID;
      fun_play_song: JNIMethodID;

   public
      constructor Create;
      procedure setup_midi_output (class_name: string);
      procedure put_short (status, data_1, data_2: integer);
      procedure play_song (file_name: string);
   end; // Class: MIDI_Output_Device //

implementation

uses
   FMX.Helpers.Android;

constructor TMIDI_Output_Device.Create;
begin
   setup_midi_output ('MIDI_Output');
end; // Create //

procedure TMIDI_Output_Device.setup_midi_output (class_name: string);
var
   javaClassName: string;
   ji: JNIInt;
   jiStatus, jiData_1, jiData_2: JNIValue;

begin
   javaClassName := Format ('org.drivers.midioutput/%s', [class_name]);
   context := SharedActivityContext;
   JavaEnv := TJNIResolver.GetJNIEnv;

   Log.d ('Loading external library from "' + test_apk_fn + '"');
   dexpath_jstring := StringToJString (test_apk_fn);

// locate/create a directory where our dex files can be put
   optimizedpath_jfile := context.getDir (StringToJString ('outdex'), TJContext.javaclass.mode_private);
   optimizedpath_jstring := optimizedpath_jfile.getAbsolutePath;

   Log.d ('Path for DEX files = ' + JStringToString (optimizedpath_jstring));
   Log.d ('APK containing target class = ' + JStringToString (dexpath_jstring));

   CL := TJDexClassLoader.JavaClass.init (dexpath_jstring, optimizedpath_jstring, nil, TJDexClassLoader.JavaClass.getSystemClassLoader);

// Test whether the Dex class is loaded, if not, exit
   if not assigned (CL) then
   begin
      Log.d ('?Failed to get DEXClassLoader');
      exit;
   end; // if

// Load the Java class
   jTempClass := CL.loadClass (StringToJString (javaClassName));
   if assigned (jTempClass) then
   begin
      jTemp := jTempClass;    // N.B You could now import the entire class
      if jTemp.QueryInterface (ILocalObject,jLocalInterface) = S_OK then
      begin
         // supports ilocalobject
         JavaObject := jTempClass.newInstance;
         oTemp := JavaObject as TObject;
         JavaObjectID := tjavaimport (otemp).GetObjectID;
         Log.d (oTemp.ClassName);

// try to access the version function from the midi_output class
         fun_version := TJNIResolver.GetJavaMethodID ((jTempClass as ILocalObject).GetObjectID, 'version', '()I');
         if not assigned (fun_version) then
         begin
           Log.d ('?fun_version not supported');
         end else
         begin
            ji := JavaEnv^.CallIntMethodA (JavaEnv, JavaObjectID, fun_version, nil);
            Log.d ('version returns ' + inttostr (ji));
         end; // if

// try to access the start function from the midi_output class
         fun_start := TJNIResolver.GetJavaMethodID ((jTempClass as ILocalObject).GetObjectID, 'start', '()V');
         if not assigned (fun_start) then
         begin
           Log.d ('?fun_start not supported');
         end else
         begin
            JavaEnv^.CallVoidMethodA (JavaEnv, JavaObjectID, fun_start, nil);
            Log.d ('fun_start found');
         end; // if

// try to access the putShort function from the midi_output class
         fun_put_short := TJNIResolver.GetJavaMethodID ((jTempClass as ILocalObject).GetObjectID, 'putShort','(III)V');
         if not assigned (fun_put_short) then
         begin
            Log.d ('?putShort not supported');
         end else
         begin
            Log.d (Format ('   @@@   putShort (%d, %d, %d)', [jiStatus.i, jiData_1.i, jiData_2.i]));
            put_short ($90, 60, 127);
         end; // if

// try to access the playSong function from the midi_output class
         fun_play_song := TJNIResolver.GetJavaMethodID ((jTempClass as ILocalObject).GetObjectID, 'playSong', '(Ljava/lang/String)V');
         if not assigned (fun_play_song) then
         begin
            Log.d ('?playSong not supported');
         end else
         begin
            Log.d ('   @@@   playSong found');
         end; // if
      end else
      begin
         Log.d ('?Could not derive ILOCALOBJECT');
      end;
   end else Log.d ('?'+javaClassname+' not found')
end; // setup_midi_output //

procedure TMIDI_Output_Device.put_short (status, data_1, data_2: integer);
var
   jiStatus, jiData_1, jiData_2: JNIValue;
   x: array of JNIOBJECT;

begin
   jiStatus.i := status;
   jiData_1.i := data_1;
   jiData_2.i := data_2;
   setLength (x, 3);
   x [0] := jiStatus.l;
   x [1] := jiData_1.l;
   x [2] := jiData_2.l;
   Log.d (Format ('putShort (%d, %d, %d)', [jiStatus.i, jiData_1.i, jiData_2.i]));
   JavaEnv^.CallVoidMethodV (JavaEnv, JavaObjectID, fun_put_short, x);
end; // put_short //

procedure TMIDI_Output_Device.play_song (file_name: string);
var
   x: array of JNIObject;
begin
   SetLength (x, 1);
   x [0] := StringToJNIString (JavaEnv, file_name);
   Log.d ('playSong (' + file_name + ')');
   JavaEnv^.CallVoidMethodV (JavaEnv, JavaObjectID, fun_play_song, x);
end; // playSong //

end. // Unit: MIDI_Output_Device //

德尔福现在知道在哪里可以找到Java类。从理论上讲,现在应该能够找到libmidi.so因为Android包是包含了必要的文件来运行的Java包的.zip文件。如果打开MIDI_Output.apk使用WinZip或WinRAR的,那么你看到这些文件。在档案中,你会发现其中包含libmidi.so的ARM 5和7平台的lib目录。当启动该程序,并有亚行-d logcat的在命令窗口中运行,亚行说,这么多的拆包MIDI_Output.apk。那么,它可能这样做,但libmidi.so不会被发现。

Delphi now knows where to find the Java classes. In theory it should now be able to find libmidi.so because an Android package is a .zip file containing the necessary files to run the Java package. If you open MIDI_Output.apk with WinZip or WinRar then you see these files. In the archive you'll find a directory lib which contains libmidi.so for the ARM 5 and 7 platforms. When launching the program and having adb -d logcat running in a command window adb says so much as unpacking MIDI_Output.apk. Well, it might do so, but libmidi.so will not be found.

Libmidi.so应该被添加到所述的\ usr \ lib中某处Android SDK中的\平台目录下。在我的情况下,完整的链接是:C:\用户\公用\文档\的RAD Studio \ 12.0 \ PlatformSDKs \的Andr​​oid NDK,R8E \平台\ Android为14 \弓臂的\ usr \ lib中。这将有助于我found从前段时间

Libmidi.so should be added to the \usr\lib somewhere under the \platforms directory of the Android SDK. The complete link in my case is: C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\android-ndk-r8e\platforms\android-14\arch-arm\usr\lib. This should help as I found out some time ago.

使用,因为我已经在这里示出了一个可以称之为MIDI功能在Delphi中产生的Andr​​oid code调用链。有关于这项技术的一些问题:

Using the call chain as I have shown here one may call MIDI functions in Delphi generated Android code. There are some questions regarding this technique:

  • 那岂不是更容易直接调用NDK的功能?它是 可能的<一个href="http://stackoverflow.com/questions/21036994/difficulties-with-calling-an-android-ndk-function-from-directly-delphi%2a%2a">call NDK功能,直接从德尔福在同一 方式DLL的。然而,类MidiDriver增加了很多功能 我不明白,在这一刻。这个功能必须 用C或Pascal程序时直接调用NDK的功能。

  • Wouldn't it be easier to call the NDK function directly? It is possible to call NDK functions directly from Delphi in the same way as DLL's. However, class MidiDriver adds a lot of functionality which I do not understand at this moment. This functionality must be programmed in C or Pascal when call the NDK functions directly.

在比尔农夫code,他使用的MediaPlayer播放MIDI 文件。可惜在MediaPlayer只能从活动进行访问, 我不知道该怎么德尔福MainActivity转移到JNI​​的Java 功能。所以这个功能不作为尚未工作。

In the code from Bill Farmer he uses the MediaPlayer to play MIDI files. Alas the MediaPlayer can only be accessed from an Activity and I do not know how to transfer the Delphi MainActivity to a JNI Java function. So this functionality does not work as of yet.

本机库被打包到.apk文件,但在这样一个没有解开 该JavaVM的检测到它的方式。现在libmidi.so必须被放 手动进的\ usr \ lib中。

Native libraries are packed into the .apk but not unpacked in such a way that the JavaVM detects it. Now the libmidi.so has to be put manually into \usr\lib.

更​​糟糕的是,硬链接必须添加到.apk文件包。该 包应该自动部署到/数据/应用-11b的 应用程序,否则创建具有JNI类的应用程序,并安装它 从Play商店似乎是不可能的。

Even worse is that a hard link must be added to the .apk package. The package should be deployed automatically to the /data/app-lib of the application, else creating an app with JNI classes and installing it from the Play Store seems impossible.

这篇关于实现MIDI与德尔福在Android的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆