PC-6001VX for IS11T その2

いきなり起動したものの、ROMフォルダを選択するとSDLの世界に戻りますので、AUDIO初期化あたりでSIGSEGVで落ちます。

W/Qt      (22339): QCommonStyle::drawComplexControl: Control 1 not handled
W/Qt      (22339): QCommonStyle::drawComplexControl: Control 1 not handled
W/Qt      (22339): QCommonStyle::drawComplexControl: Control 1 not handled
D/Qt      (22339): keyUp 
W/Qt      (22339): QCommonStyle::drawComplexControl: Control 1 not handled
D/Qt      (22339): keyDown 
D/Qt      (22339): keyUp 
D/Qt      (22339): resetSoftwareKeyboard 
D/Qt      (22339): hideSoftwareKeyboard 
V/SDL     (22339): SDL audio: opening device

実は、AndroidSDLは完全なNativeではなくVIDEO、AUDIO、KEYBOARDなどの入出力はJavaで実装していて、JNIでCからJavaを呼び出しています。(逆にJavaからCの呼び出しもあります)

という訳でまずは、AUDIO周りのJNIを修正していきます。

SDL_android.cpp

/*******************************************************************************
                               Globals
*******************************************************************************/
///static JNIEnv* mEnv = NULL;
static JNIEnv* mAudioEnv = NULL;
static JavaVM* s_javaVM = NULL;

(省略)

// Library init
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4) != JNI_OK) {
        __android_log_print(ANDROID_LOG_INFO, "SDL", "Can't get the enviroument");
        return -1;
    }

    s_javaVM = vm;
    // search for our class
    jclass clazz=env->FindClass("org/kde/necessitas/origo/QtActivity");
    if (!clazz)
    {
        __android_log_print(ANDROID_LOG_INFO, "SDL", "Can't find QtActivity class");
        return -1;
    }
    mActivityClass = (jclass)env->NewGlobalRef(clazz);

///}
/// Called before SDL_main() to initialize JNI bindings
/// extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls)
/// {
        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
/// 
///     mEnv = env;
///     mActivityClass = cls;
/// 
///     midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass,
///                                 "createGLContext","(II)Z");
///     midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass,
///                                 "flipBuffers","()V");
///     midAudioInit = mEnv->GetStaticMethodID(mActivityClass, 
///                                 "audioInit", "(IZZI)Ljava/lang/Object;");
        midAudioInit = env->GetStaticMethodID(mActivityClass, "audioInit", "(IZZI)Ljava/lang/Object;");
///     midAudioWriteShortBuffer = mEnv->GetStaticMethodID(mActivityClass,
///                                 "audioWriteShortBuffer", "([S)V");
        midAudioWriteShortBuffer = env->GetStaticMethodID(mActivityClass, "audioWriteShortBuffer", "([S)V");
///     midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass,
///                                 "audioWriteByteBuffer", "([B)V");
        midAudioWriteByteBuffer = env->GetStaticMethodID(mActivityClass, "audioWriteByteBuffer", "([B)V");
///     midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
///                                 "audioQuit", "()V");
        midAudioQuit = env->GetStaticMethodID(mActivityClass, "audioQuit", "()V");
    
///     if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
///            !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
        if(!midAudioInit || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
            __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
        }
        return JNI_VERSION_1_4;
}

(省略)


/// extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
extern "C" void Java_org_kde_necessitas_origo_QtActivity_nativeRunAudioThread(
                                    JNIEnv* env, jclass cls)
{
    /* This is the audio thread, with a different environment */
    mAudioEnv = env;

    Android_RunAudioThread();
}


/*******************************************************************************
             Functions called by SDL into Java
*******************************************************************************/
extern "C" SDL_bool Android_JNI_CreateContext(int majorVersion, int minorVersion)
{
///    if (mEnv->CallStaticBooleanMethod(mActivityClass, midCreateGLContext, majorVersion, minorVersion)) {
///        return SDL_TRUE;
///    } else {
     return SDL_FALSE;
///    }
}

extern "C" void Android_JNI_SwapWindow()
{
///    mEnv->CallStaticVoidMethod(mActivityClass, midFlipBuffers);
}

extern "C" void Android_JNI_SetActivityTitle(const char *title)
{
///    jmethodID mid;
///
///    mid = mEnv->GetStaticMethodID(mActivityClass,"setActivityTitle","(Ljava/lang/String;)V");
///    if (mid) {
///        mEnv->CallStaticVoidMethod(mActivityClass, mid, mEnv->NewStringUTF(title));
///    }
}

(省略)

extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
{
    int audioBufferFrames;

    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
    audioBuffer16Bit = is16Bit;
    audioBufferStereo = channelCount > 1;

///    audioBuffer = mEnv->CallStaticObjectMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
    JNIEnv* env;
    if (s_javaVM->AttachCurrentThread(&env, NULL) < 0)
    {
        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AttachCurrentThread failed");
        return 0;
    }
    audioBuffer = env->CallStaticObjectMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);

    if (audioBuffer == NULL) {
        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: didn't get back a good audio buffer!");
        return 0;
    }
///    audioBuffer = mEnv->NewGlobalRef(audioBuffer);
    audioBuffer = env->NewGlobalRef(audioBuffer);

    jboolean isCopy = JNI_FALSE;
    if (audioBuffer16Bit) {
///        audioBufferPinned = mEnv->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
///        audioBufferFrames = mEnv->GetArrayLength((jshortArray)audioBuffer);
        audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
        audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer);
    } else {
///        audioBufferPinned = mEnv->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
///        audioBufferFrames = mEnv->GetArrayLength((jbyteArray)audioBuffer);
        audioBufferPinned = env->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
        audioBufferFrames = env->GetArrayLength((jbyteArray)audioBuffer);
    }
    if (audioBufferStereo) {
        audioBufferFrames /= 2;
    }

    s_javaVM->DetachCurrentThread();
    return audioBufferFrames;
}

(省略)

extern "C" void Android_JNI_CloseAudioDevice()
{
    JNIEnv* env;
    if (s_javaVM->AttachCurrentThread(&env, NULL) < 0)
    {
        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AttachCurrentThread failed");
        return;
    }
///    mEnv->CallStaticVoidMethod(mActivityClass, midAudioQuit); 
    env->CallStaticVoidMethod(mActivityClass, midAudioQuit);

    if (audioBuffer) {
///        mEnv->DeleteGlobalRef(audioBuffer);
        env->DeleteGlobalRef(audioBuffer);
        audioBuffer = NULL;
        audioBufferPinned = NULL;
    }
    s_javaVM->DetachCurrentThread();
}

QtActivity.java

//@ANDROID-11
//QtCreator import android.app.Fragment;
//QtCreator import android.view.ActionMode;
//QtCreator import android.view.ActionMode.Callback;
//@ANDROID-11

import android.app.*;
import android.content.*;
import android.view.*;
import android.os.*;
import android.util.Log;
import android.graphics.*;
import android.text.method.*;
import android.text.*;
import android.media.*;
import android.hardware.*;
import java.lang.*;

(省略)

    /// C functions we call
    public static native void nativeRunAudioThread();

    private static Thread mAudioThread;
    private static AudioTrack mAudioTrack;

    /* Java functions called from C */

    private static Object buf;

    public static Object audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);

        Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");

        // Let the user pick a larger buffer if they really want -- but ye
        // gods they probably shouldn't, the minimums are horrifyingly high
        // latency already
        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);

        mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);

        audioStartThread();

        Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");

        if (is16Bit) {
            buf = new short[desiredFrames * (isStereo ? 2 : 1)];
        } else {
            buf = new byte[desiredFrames * (isStereo ? 2 : 1)];
        }
        return buf;
    }

    public static void audioStartThread() {
        mAudioThread = new Thread(new Runnable() {
            public void run() {
                mAudioTrack.play();
                nativeRunAudioThread();
            }
        });

        // I'd take REALTIME if I could get it!
        mAudioThread.setPriority(Thread.MAX_PRIORITY);
        mAudioThread.start();
    }

    public static void audioWriteShortBuffer(short[] buffer) {
        for (int i = 0; i < buffer.length; ) {
            int result = mAudioTrack.write(buffer, i, buffer.length - i);
            if (result > 0) {
                i += result;
            } else if (result == 0) {
                try {
                    Thread.sleep(1);
                } catch(InterruptedException e) {
                    // Nom nom
                }
            } else {
                Log.w("SDL", "SDL audio: error return from write(short)");
                return;
            }
        }
    }

    public static void audioWriteByteBuffer(byte[] buffer) {
        for (int i = 0; i < buffer.length; ) {
            int result = mAudioTrack.write(buffer, i, buffer.length - i);
            if (result > 0) {
                i += result;
            } else if (result == 0) {
                try {
                    Thread.sleep(1);
                } catch(InterruptedException e) {
                    // Nom nom
                }
            } else {
                Log.w("SDL", "SDL audio: error return from write(short)");
                return;
            }
        }
    }

    public static void audioQuit() {
        if (mAudioThread != null) {
            try {
                mAudioThread.join();
            } catch(Exception e) {
                Log.v("SDL", "Problem stopping audio thread: " + e);
            }
            mAudioThread = null;

            //Log.v("SDL", "Finished waiting for audio thread");
        }

        if (mAudioTrack != null) {
            mAudioTrack.stop();
            mAudioTrack = null;
        }
    }

SIGSEGVでは落ちなくなりましたが初期化で失敗しています。