Home | History | Annotate | Download | only in soundpool
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdio.h>
     18 
     19 //#define LOG_NDEBUG 0
     20 #define LOG_TAG "SoundPool-JNI"
     21 
     22 #include <utils/Log.h>
     23 #include <nativehelper/jni.h>
     24 #include <nativehelper/JNIHelp.h>
     25 #include <android_runtime/AndroidRuntime.h>
     26 #include "SoundPool.h"
     27 
     28 using namespace android;
     29 
     30 static struct fields_t {
     31     jfieldID    mNativeContext;
     32     jmethodID   mPostEvent;
     33     jclass      mSoundPoolClass;
     34 } fields;
     35 
     36 static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
     37     return (SoundPool*)env->GetIntField(thiz, fields.mNativeContext);
     38 }
     39 
     40 // ----------------------------------------------------------------------------
     41 static int
     42 android_media_SoundPool_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
     43 {
     44     LOGV("android_media_SoundPool_load_URL");
     45     SoundPool *ap = MusterSoundPool(env, thiz);
     46     if (path == NULL) {
     47         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
     48         return 0;
     49     }
     50     const char* s = env->GetStringUTFChars(path, NULL);
     51     int id = ap->load(s, priority);
     52     env->ReleaseStringUTFChars(path, s);
     53     return id;
     54 }
     55 
     56 static int
     57 android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
     58         jlong offset, jlong length, jint priority)
     59 {
     60     LOGV("android_media_SoundPool_load_FD");
     61     SoundPool *ap = MusterSoundPool(env, thiz);
     62     if (ap == NULL) return 0;
     63     return ap->load(getParcelFileDescriptorFD(env, fileDescriptor),
     64             int64_t(offset), int64_t(length), int(priority));
     65 }
     66 
     67 static bool
     68 android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
     69     LOGV("android_media_SoundPool_unload\n");
     70     SoundPool *ap = MusterSoundPool(env, thiz);
     71     if (ap == NULL) return 0;
     72     return ap->unload(sampleID);
     73 }
     74 
     75 static int
     76 android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
     77         jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
     78         jfloat rate)
     79 {
     80     LOGV("android_media_SoundPool_play\n");
     81     SoundPool *ap = MusterSoundPool(env, thiz);
     82     if (ap == NULL) return 0;
     83     return ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
     84 }
     85 
     86 static void
     87 android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
     88 {
     89     LOGV("android_media_SoundPool_pause");
     90     SoundPool *ap = MusterSoundPool(env, thiz);
     91     if (ap == NULL) return;
     92     ap->pause(channelID);
     93 }
     94 
     95 static void
     96 android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
     97 {
     98     LOGV("android_media_SoundPool_resume");
     99     SoundPool *ap = MusterSoundPool(env, thiz);
    100     if (ap == NULL) return;
    101     ap->resume(channelID);
    102 }
    103 
    104 static void
    105 android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
    106 {
    107     LOGV("android_media_SoundPool_autoPause");
    108     SoundPool *ap = MusterSoundPool(env, thiz);
    109     if (ap == NULL) return;
    110     ap->autoPause();
    111 }
    112 
    113 static void
    114 android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
    115 {
    116     LOGV("android_media_SoundPool_autoResume");
    117     SoundPool *ap = MusterSoundPool(env, thiz);
    118     if (ap == NULL) return;
    119     ap->autoResume();
    120 }
    121 
    122 static void
    123 android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
    124 {
    125     LOGV("android_media_SoundPool_stop");
    126     SoundPool *ap = MusterSoundPool(env, thiz);
    127     if (ap == NULL) return;
    128     ap->stop(channelID);
    129 }
    130 
    131 static void
    132 android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
    133         float leftVolume, float rightVolume)
    134 {
    135     LOGV("android_media_SoundPool_setVolume");
    136     SoundPool *ap = MusterSoundPool(env, thiz);
    137     if (ap == NULL) return;
    138     ap->setVolume(channelID, leftVolume, rightVolume);
    139 }
    140 
    141 static void
    142 android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
    143         int priority)
    144 {
    145     LOGV("android_media_SoundPool_setPriority");
    146     SoundPool *ap = MusterSoundPool(env, thiz);
    147     if (ap == NULL) return;
    148     ap->setPriority(channelID, priority);
    149 }
    150 
    151 static void
    152 android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
    153         int loop)
    154 {
    155     LOGV("android_media_SoundPool_setLoop");
    156     SoundPool *ap = MusterSoundPool(env, thiz);
    157     if (ap == NULL) return;
    158     ap->setLoop(channelID, loop);
    159 }
    160 
    161 static void
    162 android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
    163         float rate)
    164 {
    165     LOGV("android_media_SoundPool_setRate");
    166     SoundPool *ap = MusterSoundPool(env, thiz);
    167     if (ap == NULL) return;
    168     ap->setRate(channelID, rate);
    169 }
    170 
    171 static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
    172 {
    173     LOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
    174     JNIEnv *env = AndroidRuntime::getJNIEnv();
    175     env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
    176 }
    177 
    178 static jint
    179 android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
    180 {
    181     LOGV("android_media_SoundPool_native_setup");
    182     SoundPool *ap = new SoundPool(maxChannels, streamType, srcQuality);
    183     if (ap == NULL) {
    184         return -1;
    185     }
    186 
    187     // save pointer to SoundPool C++ object in opaque field in Java object
    188     env->SetIntField(thiz, fields.mNativeContext, (int)ap);
    189 
    190     // set callback with weak reference
    191     jobject globalWeakRef = env->NewGlobalRef(weakRef);
    192     ap->setCallback(android_media_callback, globalWeakRef);
    193     return 0;
    194 }
    195 
    196 static void
    197 android_media_SoundPool_release(JNIEnv *env, jobject thiz)
    198 {
    199     LOGV("android_media_SoundPool_release");
    200     SoundPool *ap = MusterSoundPool(env, thiz);
    201     if (ap != NULL) {
    202 
    203         // release weak reference
    204         jobject weakRef = (jobject) ap->getUserData();
    205         if (weakRef != NULL) {
    206             env->DeleteGlobalRef(weakRef);
    207         }
    208 
    209         // clear callback and native context
    210         ap->setCallback(NULL, NULL);
    211         env->SetIntField(thiz, fields.mNativeContext, 0);
    212         delete ap;
    213     }
    214 }
    215 
    216 // ----------------------------------------------------------------------------
    217 
    218 // Dalvik VM type signatures
    219 static JNINativeMethod gMethods[] = {
    220     {   "_load",
    221         "(Ljava/lang/String;I)I",
    222         (void *)android_media_SoundPool_load_URL
    223     },
    224     {   "_load",
    225         "(Ljava/io/FileDescriptor;JJI)I",
    226         (void *)android_media_SoundPool_load_FD
    227     },
    228     {   "unload",
    229         "(I)Z",
    230         (void *)android_media_SoundPool_unload
    231     },
    232     {   "play",
    233         "(IFFIIF)I",
    234         (void *)android_media_SoundPool_play
    235     },
    236     {   "pause",
    237         "(I)V",
    238         (void *)android_media_SoundPool_pause
    239     },
    240     {   "resume",
    241         "(I)V",
    242         (void *)android_media_SoundPool_resume
    243     },
    244     {   "autoPause",
    245         "()V",
    246         (void *)android_media_SoundPool_autoPause
    247     },
    248     {   "autoResume",
    249         "()V",
    250         (void *)android_media_SoundPool_autoResume
    251     },
    252     {   "stop",
    253         "(I)V",
    254         (void *)android_media_SoundPool_stop
    255     },
    256     {   "setVolume",
    257         "(IFF)V",
    258         (void *)android_media_SoundPool_setVolume
    259     },
    260     {   "setPriority",
    261         "(II)V",
    262         (void *)android_media_SoundPool_setPriority
    263     },
    264     {   "setLoop",
    265         "(II)V",
    266         (void *)android_media_SoundPool_setLoop
    267     },
    268     {   "setRate",
    269         "(IF)V",
    270         (void *)android_media_SoundPool_setRate
    271     },
    272     {   "native_setup",
    273         "(Ljava/lang/Object;III)I",
    274         (void*)android_media_SoundPool_native_setup
    275     },
    276     {   "release",
    277         "()V",
    278         (void*)android_media_SoundPool_release
    279     }
    280 };
    281 
    282 static const char* const kClassPathName = "android/media/SoundPool";
    283 
    284 jint JNI_OnLoad(JavaVM* vm, void* reserved)
    285 {
    286     JNIEnv* env = NULL;
    287     jint result = -1;
    288     jclass clazz;
    289 
    290     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    291         LOGE("ERROR: GetEnv failed\n");
    292         goto bail;
    293     }
    294     assert(env != NULL);
    295 
    296     clazz = env->FindClass(kClassPathName);
    297     if (clazz == NULL) {
    298         LOGE("Can't find %s", kClassPathName);
    299         goto bail;
    300     }
    301 
    302     fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I");
    303     if (fields.mNativeContext == NULL) {
    304         LOGE("Can't find SoundPool.mNativeContext");
    305         goto bail;
    306     }
    307 
    308     fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
    309                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    310     if (fields.mPostEvent == NULL) {
    311         LOGE("Can't find android/media/SoundPool.postEventFromNative");
    312         goto bail;
    313     }
    314 
    315     // create a reference to class. Technically, we're leaking this reference
    316     // since it's a static object.
    317     fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
    318 
    319     if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
    320         goto bail;
    321 
    322     /* success -- return valid version number */
    323     result = JNI_VERSION_1_4;
    324 
    325 bail:
    326     return result;
    327 }
    328