2014-01-15 75 views
4

我在尋找一段時間以在Delphi XE5中以Android爲目標播放MIDI的方式。我之前的幾個問題都與這個「追求」有關:-)。我向embarcadero提交了兩個請求:#119422爲TMediaPlayer增加了MIDI支持,#119423向Firemonkey添加了MIDI框架,但這並沒有幫助。我終於成功了。據我所知,還有更多的人在Android上尋找MIDI我發佈這個問題的答案文件。在Android上使用Delphi實現MIDI

+1

你沒有使用自定義NDK橋接嗎? http://stackoverflow.com/questions/21036994/ –

+0

@ Arioch'The - 我遇到了一些麻煩,在我報告之前,我必須先探討一些麻煩。我的第一個目標是獲得一些工作,這就是我現在擁有的。下一個目標是簡化工作。但從長遠來看,我希望報告直接將C例程調用到SoniVox。我不介意如果有人打敗了;-) – Arnold

回答

9

Android系統有一個內部MIDI合成器。您可以通過Android NDK訪問它。我在article containing some downloads中描述過這個。這個答案是這篇文章的簡短描述。你會在這裏看到的是一個概念證明。它將演示如何在Android系統上播放MIDI音符,但需要改進。歡迎提出改進建議:-)

使用Eclipse與Java項目進行接口。我認爲你的Delphi XE5包含Mobile Pack,它提供了兩種已安裝的東西:Android SDK和NDK。 請勿通過從Google下載完整的Android SDK來重新安裝這些內容。 Download and install Eclipse Android開發工具(ADT)插件並按照安裝說明操作。這允許您使用Delphi XE5已經安裝的Android SDK/NDK環境(您可以在Delphi,Options | Tools | SDK Manager中找到路徑)。通過這種方式,Delphi和Eclipse將共享相同的SDK和NDK。

我使用了MIDI library developed by Willam Farmer。他還擁有完整的SoniVox文檔,我無法在其他地方找到它。他的驅動程序帶有一個完整的示例(Java)程序。我創建了自己的項目,並將包名稱更改爲org.drivers.midioutput,因此所有函數都以Java_org_drivers_midioutput_MidiDriver_(請參閱下面的代碼)爲前綴。

當你希望編譯midi.c的時候打開一個命令窗口,然後在項目目錄中調用ndk-build。有些錯誤信息是可以的。 mips和x86庫不是在我的情況下構建的。

雖然你應該知道一點:ndk的路徑可能不包含空格。當你讓Delphi安裝程序安裝Delphi時,其中必然會有一個空間:子目錄Rad Studio,其中包含Delphi安裝SDK和NDK的可怕長文件名。爲了解決此問題,請在驅動器C:上創建一個空目錄,將其稱爲C:\ ndk。使用MKLINK將此目錄鏈接到ndk目錄。這隻能通過提升的命令提示符來完成,因爲您這樣做會丟失網絡連接。該鏈接是持久的,所以只需關閉命令提示符並打開另一個,未升級的鏈接,現在都應該可以工作。現在你可以真正使用ndk-build。

midi.c - 與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接口NDK-建設,這將前綴以lib編譯庫,並通過替換的.so延長。所以midi.c會編譯成libmidi.so。編譯過的庫被添加到下載中,所以你不需要編譯midi.c.

MidiDriver.Java聲明瞭一個接口,一個audioTrack和一個線程來處理所有這些。我並沒有煞費苦心去找到這個工作的方式。因爲我不知道如何處理接口,所以在Delphi中我爲MidiDriver創建了一個Java包裝:class MIDI_Output。該類用於與Delphi進行交互。

MidiDriver類是Java和調用SoniVox函數的C函數之間的接口。 MIDI_Output類是Java和Delphi之間的接口。 MIDI_Output創建一個MidiDriver的實例。

類MidiDriver - 與所述接口的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

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「,」*** message「)語句,以查看出錯的地方。如果您不喜歡它們,請將它們移除,但是如果您對Android不瞭解(我仍然在很大程度上),那麼查看您的應用程序中發生的情況是非常有用的方法。日誌可以在Delphi中運行,請參閱Delphi源代碼。

當程序編譯良好時,您的項目\ bin目錄中有一個MIDI_Output.apk軟件包。這個包將被Delphi用來運行Java方法。

Java可以通過使用JNI從Delphi進行訪問。在RedTitan網站上可以找到實踐教程。本教程的思想是在類TMIDI_Output_Device中實現的。

您可能會看到一個常量字符串test_apk_fn是通過MIDI_Output.apk Android包的路徑定義的。該字符串爲JNI提供了可以找到Java庫的名稱。字符串javaClassName提供了與Java接口所需的包名稱。通過這些字符串,Delphi JNI可以找到所需的類。

類TMIDI_Output_Device - 爲Java類提供一個Delphi包裝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包是一個.zip文件,其中包含運行Java包的必要文件。如果用WinZip或WinRar打開MIDI_Output.apk,則會看到這些文件。在存檔中,您將找到一個包含ARM 5和7平臺的libmidi.so的目錄庫。啓動程序並在命令窗口中運行adb -d logcat時,adb會說明解壓縮MIDI_Output.apk。那麼,它可能會這樣做,但不會找到libmidi.so。

Libmidi.so應該添加到Android SDK的\ platforms目錄下的\ usr \ lib目錄下。在我的情況下,完整的鏈接是:C:\ Users \ Public \ Documents \ RAD Studio \ 12.0 \ PlatformSDKs \ android-ndk-r8e \ platforms \ android-14 \ arch-arm \ usr \ lib。這應該有所幫助,因爲我found out some time ago

使用我在這裏顯示的調用鏈,可以在Delphi生成的Android代碼中調用MIDI函數。有關於該技術的一些問題:

  • 那豈不是更容易直接調用NDK的功能?這是 可能到call NDK functions directly from Delphi在相同的 方式爲DLL的。但是,MidiDriver類添加了很多功能 ,我現在還不明白。在直接調用NDK函數時,此功能必須在C或Pascal中編程爲 。

  • 在Bill Farmer的代碼中,他使用MediaPlayer播放MIDI 文件。唉MediaPlayer只能從一個活動和 我不知道如何將Delphi MainActivity轉移到一個JNI Java 函數。所以這個功能目前還不行。

  • 本地庫被打包到.apk中,但未以JavaVM檢測它的方式解壓縮到這樣的一種 。現在libmidi.so必須手動將 放入\ usr \ lib中。

  • 更糟的是硬鏈接必須添加到.apk包中。 包應該自動部署到 應用程序的/ data/app-lib,否則創建一個帶有JNI類的應用程序並從Play商店安裝 似乎是不可能的。

2

另一種方法是使用本機Android版本的BASS plus BASSMIDI插件。有很好的示例代碼。伊恩的支持非常好。你可以在這裏找到它們:

BASS lib中的Android:http://www.un4seen.com/forum/?topic=13225

BASSMIDI插件(d/L):http://www.un4seen.com/download.php?bassmidi24-linux

有他的網站上一個.NET版本,和第3 Sourceforge公司將API公開給Java。我不允許發佈超過兩個鏈接(但),但你可以通過快速搜索nativebass找到它

相當晚的答案,但它可能仍然有助於其他人尋找更快的方法或工程僅在德爾福。

+0

謝謝,上面的過程的任何簡化,歡迎:-) – Arnold