src/core/android/SDL_android.c
changeset 10280 985f92b2f8e9
parent 10199 6ab154366a9b
child 10296 80190c2a271d
     1.1 --- a/src/core/android/SDL_android.c	Wed Aug 10 16:00:16 2016 -0400
     1.2 +++ b/src/core/android/SDL_android.c	Thu Aug 11 22:04:49 2016 -0400
     1.3 @@ -71,10 +71,14 @@
     1.4  
     1.5  /* method signatures */
     1.6  static jmethodID midGetNativeSurface;
     1.7 -static jmethodID midAudioInit;
     1.8 +static jmethodID midAudioOpen;
     1.9  static jmethodID midAudioWriteShortBuffer;
    1.10  static jmethodID midAudioWriteByteBuffer;
    1.11 -static jmethodID midAudioQuit;
    1.12 +static jmethodID midAudioClose;
    1.13 +static jmethodID midCaptureOpen;
    1.14 +static jmethodID midCaptureReadShortBuffer;
    1.15 +static jmethodID midCaptureReadByteBuffer;
    1.16 +static jmethodID midCaptureClose;
    1.17  static jmethodID midPollInputDevices;
    1.18  
    1.19  /* Accelerometer data storage */
    1.20 @@ -118,21 +122,31 @@
    1.21  
    1.22      midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.23                                  "getNativeSurface","()Landroid/view/Surface;");
    1.24 -    midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.25 -                                "audioInit", "(IZZI)I");
    1.26 +    midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.27 +                                "audioOpen", "(IZZI)I");
    1.28      midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.29                                  "audioWriteShortBuffer", "([S)V");
    1.30      midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.31                                  "audioWriteByteBuffer", "([B)V");
    1.32 -    midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.33 -                                "audioQuit", "()V");
    1.34 +    midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.35 +                                "audioClose", "()V");
    1.36 +    midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.37 +                                "captureOpen", "(IZZI)I");
    1.38 +    midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.39 +                                "captureReadShortBuffer", "([SZ)I");
    1.40 +    midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.41 +                                "captureReadByteBuffer", "([BZ)I");
    1.42 +    midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.43 +                                "captureClose", "()V");
    1.44      midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
    1.45                                  "pollInputDevices", "()V");
    1.46  
    1.47      bHasNewData = SDL_FALSE;
    1.48  
    1.49 -    if (!midGetNativeSurface || !midAudioInit ||
    1.50 -       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit || !midPollInputDevices) {
    1.51 +    if (!midGetNativeSurface ||
    1.52 +       !midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose || !
    1.53 +       !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose ||
    1.54 +       !midPollInputDevices) {
    1.55          __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
    1.56      }
    1.57      __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
    1.58 @@ -556,11 +570,14 @@
    1.59  static jboolean audioBuffer16Bit = JNI_FALSE;
    1.60  static jobject audioBuffer = NULL;
    1.61  static void* audioBufferPinned = NULL;
    1.62 +static jboolean captureBuffer16Bit = JNI_FALSE;
    1.63 +static jobject captureBuffer = NULL;
    1.64  
    1.65 -int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
    1.66 +int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
    1.67  {
    1.68      jboolean audioBufferStereo;
    1.69      int audioBufferFrames;
    1.70 +    jobject jbufobj = NULL;
    1.71      jboolean isCopy;
    1.72  
    1.73      JNIEnv *env = Android_JNI_GetEnv();
    1.74 @@ -570,14 +587,24 @@
    1.75      }
    1.76      Android_JNI_SetupThread();
    1.77  
    1.78 -    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
    1.79 -    audioBuffer16Bit = is16Bit;
    1.80      audioBufferStereo = channelCount > 1;
    1.81  
    1.82 -    if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
    1.83 -        /* Error during audio initialization */
    1.84 -        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
    1.85 -        return 0;
    1.86 +    if (iscapture) {
    1.87 +        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
    1.88 +        captureBuffer16Bit = is16Bit;
    1.89 +        if ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
    1.90 +            /* Error during audio initialization */
    1.91 +            __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
    1.92 +            return 0;
    1.93 +        }
    1.94 +    } else {
    1.95 +        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
    1.96 +        audioBuffer16Bit = is16Bit;
    1.97 +        if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
    1.98 +            /* Error during audio initialization */
    1.99 +            __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
   1.100 +            return 0;
   1.101 +        }
   1.102      }
   1.103  
   1.104      /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
   1.105 @@ -586,31 +613,43 @@
   1.106      if (is16Bit) {
   1.107          jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
   1.108          if (audioBufferLocal) {
   1.109 -            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
   1.110 +            jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
   1.111              (*env)->DeleteLocalRef(env, audioBufferLocal);
   1.112          }
   1.113      }
   1.114      else {
   1.115          jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
   1.116          if (audioBufferLocal) {
   1.117 -            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
   1.118 +            jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
   1.119              (*env)->DeleteLocalRef(env, audioBufferLocal);
   1.120          }
   1.121      }
   1.122  
   1.123 -    if (audioBuffer == NULL) {
   1.124 +    if (jbufobj == NULL) {
   1.125          __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
   1.126          return 0;
   1.127      }
   1.128  
   1.129 +    if (iscapture) {
   1.130 +        captureBuffer = jbufobj;
   1.131 +    } else {
   1.132 +        audioBuffer = jbufobj;
   1.133 +    }
   1.134 +
   1.135      isCopy = JNI_FALSE;
   1.136 -    if (audioBuffer16Bit) {
   1.137 -        audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
   1.138 +
   1.139 +    if (is16Bit) {
   1.140 +        if (!iscapture) {
   1.141 +            audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
   1.142 +        }
   1.143          audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
   1.144      } else {
   1.145 -        audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
   1.146 +        if (!iscapture) {
   1.147 +            audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
   1.148 +        }
   1.149          audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
   1.150      }
   1.151 +
   1.152      if (audioBufferStereo) {
   1.153          audioBufferFrames /= 2;
   1.154      }
   1.155 @@ -638,16 +677,73 @@
   1.156      /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
   1.157  }
   1.158  
   1.159 -void Android_JNI_CloseAudioDevice(void)
   1.160 +int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
   1.161 +{
   1.162 +    JNIEnv *env = Android_JNI_GetEnv();
   1.163 +    jboolean isCopy = JNI_FALSE;
   1.164 +    jint br;
   1.165 +
   1.166 +    if (captureBuffer16Bit) {
   1.167 +        SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2));
   1.168 +        br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
   1.169 +        if (br > 0) {
   1.170 +            jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
   1.171 +            br *= 2;
   1.172 +            SDL_memcpy(buffer, ptr, br);
   1.173 +            (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
   1.174 +        }
   1.175 +    } else {
   1.176 +        SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
   1.177 +        br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
   1.178 +        if (br > 0) {
   1.179 +            jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
   1.180 +            SDL_memcpy(buffer, ptr, br);
   1.181 +            (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
   1.182 +        }
   1.183 +    }
   1.184 +
   1.185 +    return (int) br;
   1.186 +}
   1.187 +
   1.188 +void Android_JNI_FlushCapturedAudio(void)
   1.189 +{
   1.190 +    JNIEnv *env = Android_JNI_GetEnv();
   1.191 +    #if 0  /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
   1.192 +    if (captureBuffer16Bit) {
   1.193 +        const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
   1.194 +        while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
   1.195 +    } else {
   1.196 +        const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
   1.197 +        while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
   1.198 +    }
   1.199 +    #else
   1.200 +    if (captureBuffer16Bit) {
   1.201 +        const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
   1.202 +        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
   1.203 +    } else {
   1.204 +        const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
   1.205 +        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
   1.206 +    }
   1.207 +    #endif
   1.208 +}
   1.209 +
   1.210 +void Android_JNI_CloseAudioDevice(const int iscapture)
   1.211  {
   1.212      JNIEnv *env = Android_JNI_GetEnv();
   1.213  
   1.214 -    (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit);
   1.215 -
   1.216 -    if (audioBuffer) {
   1.217 -        (*env)->DeleteGlobalRef(env, audioBuffer);
   1.218 -        audioBuffer = NULL;
   1.219 -        audioBufferPinned = NULL;
   1.220 +    if (iscapture) {
   1.221 +        (*env)->CallStaticVoidMethod(env, mActivityClass, midCaptureClose);
   1.222 +        if (captureBuffer) {
   1.223 +            (*env)->DeleteGlobalRef(env, captureBuffer);
   1.224 +            captureBuffer = NULL;
   1.225 +        }
   1.226 +    } else {
   1.227 +        (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioClose);
   1.228 +        if (audioBuffer) {
   1.229 +            (*env)->DeleteGlobalRef(env, audioBuffer);
   1.230 +            audioBuffer = NULL;
   1.231 +            audioBufferPinned = NULL;
   1.232 +        }
   1.233      }
   1.234  }
   1.235