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 <media/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 static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
     36     return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
     37 }
     38 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
     39 struct audio_attributes_fields_t {
     40     jfieldID  fieldUsage;        // AudioAttributes.mUsage
     41     jfieldID  fieldContentType;  // AudioAttributes.mContentType
     42     jfieldID  fieldFlags;        // AudioAttributes.mFlags
     43     jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
     44 };
     45 static audio_attributes_fields_t javaAudioAttrFields;
     46 
     47 // ----------------------------------------------------------------------------
     48 static jint
     49 android_media_SoundPool_SoundPoolImpl_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
     50 {
     51     ALOGV("android_media_SoundPool_SoundPoolImpl_load_URL");
     52     SoundPool *ap = MusterSoundPool(env, thiz);
     53     if (path == NULL) {
     54         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
     55         return 0;
     56     }
     57     const char* s = env->GetStringUTFChars(path, NULL);
     58     int id = ap->load(s, priority);
     59     env->ReleaseStringUTFChars(path, s);
     60     return (jint) id;
     61 }
     62 
     63 static jint
     64 android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
     65         jlong offset, jlong length, jint priority)
     66 {
     67     ALOGV("android_media_SoundPool_SoundPoolImpl_load_FD");
     68     SoundPool *ap = MusterSoundPool(env, thiz);
     69     if (ap == NULL) return 0;
     70     return (jint) ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
     71             int64_t(offset), int64_t(length), int(priority));
     72 }
     73 
     74 static jboolean
     75 android_media_SoundPool_SoundPoolImpl_unload(JNIEnv *env, jobject thiz, jint sampleID) {
     76     ALOGV("android_media_SoundPool_SoundPoolImpl_unload\n");
     77     SoundPool *ap = MusterSoundPool(env, thiz);
     78     if (ap == NULL) return JNI_FALSE;
     79     return ap->unload(sampleID) ? JNI_TRUE : JNI_FALSE;
     80 }
     81 
     82 static jint
     83 android_media_SoundPool_SoundPoolImpl_play(JNIEnv *env, jobject thiz, jint sampleID,
     84         jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
     85         jfloat rate)
     86 {
     87     ALOGV("android_media_SoundPool_SoundPoolImpl_play\n");
     88     SoundPool *ap = MusterSoundPool(env, thiz);
     89     if (ap == NULL) return 0;
     90     return (jint) ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
     91 }
     92 
     93 static void
     94 android_media_SoundPool_SoundPoolImpl_pause(JNIEnv *env, jobject thiz, jint channelID)
     95 {
     96     ALOGV("android_media_SoundPool_SoundPoolImpl_pause");
     97     SoundPool *ap = MusterSoundPool(env, thiz);
     98     if (ap == NULL) return;
     99     ap->pause(channelID);
    100 }
    101 
    102 static void
    103 android_media_SoundPool_SoundPoolImpl_resume(JNIEnv *env, jobject thiz, jint channelID)
    104 {
    105     ALOGV("android_media_SoundPool_SoundPoolImpl_resume");
    106     SoundPool *ap = MusterSoundPool(env, thiz);
    107     if (ap == NULL) return;
    108     ap->resume(channelID);
    109 }
    110 
    111 static void
    112 android_media_SoundPool_SoundPoolImpl_autoPause(JNIEnv *env, jobject thiz)
    113 {
    114     ALOGV("android_media_SoundPool_SoundPoolImpl_autoPause");
    115     SoundPool *ap = MusterSoundPool(env, thiz);
    116     if (ap == NULL) return;
    117     ap->autoPause();
    118 }
    119 
    120 static void
    121 android_media_SoundPool_SoundPoolImpl_autoResume(JNIEnv *env, jobject thiz)
    122 {
    123     ALOGV("android_media_SoundPool_SoundPoolImpl_autoResume");
    124     SoundPool *ap = MusterSoundPool(env, thiz);
    125     if (ap == NULL) return;
    126     ap->autoResume();
    127 }
    128 
    129 static void
    130 android_media_SoundPool_SoundPoolImpl_stop(JNIEnv *env, jobject thiz, jint channelID)
    131 {
    132     ALOGV("android_media_SoundPool_SoundPoolImpl_stop");
    133     SoundPool *ap = MusterSoundPool(env, thiz);
    134     if (ap == NULL) return;
    135     ap->stop(channelID);
    136 }
    137 
    138 static void
    139 android_media_SoundPool_SoundPoolImpl_setVolume(JNIEnv *env, jobject thiz, jint channelID,
    140         jfloat leftVolume, jfloat rightVolume)
    141 {
    142     ALOGV("android_media_SoundPool_SoundPoolImpl_setVolume");
    143     SoundPool *ap = MusterSoundPool(env, thiz);
    144     if (ap == NULL) return;
    145     ap->setVolume(channelID, (float) leftVolume, (float) rightVolume);
    146 }
    147 
    148 static void
    149 android_media_SoundPool_SoundPoolImpl_setPriority(JNIEnv *env, jobject thiz, jint channelID,
    150         jint priority)
    151 {
    152     ALOGV("android_media_SoundPool_SoundPoolImpl_setPriority");
    153     SoundPool *ap = MusterSoundPool(env, thiz);
    154     if (ap == NULL) return;
    155     ap->setPriority(channelID, (int) priority);
    156 }
    157 
    158 static void
    159 android_media_SoundPool_SoundPoolImpl_setLoop(JNIEnv *env, jobject thiz, jint channelID,
    160         int loop)
    161 {
    162     ALOGV("android_media_SoundPool_SoundPoolImpl_setLoop");
    163     SoundPool *ap = MusterSoundPool(env, thiz);
    164     if (ap == NULL) return;
    165     ap->setLoop(channelID, loop);
    166 }
    167 
    168 static void
    169 android_media_SoundPool_SoundPoolImpl_setRate(JNIEnv *env, jobject thiz, jint channelID,
    170        jfloat rate)
    171 {
    172     ALOGV("android_media_SoundPool_SoundPoolImpl_setRate");
    173     SoundPool *ap = MusterSoundPool(env, thiz);
    174     if (ap == NULL) return;
    175     ap->setRate(channelID, (float) rate);
    176 }
    177 
    178 static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
    179 {
    180     ALOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
    181     JNIEnv *env = AndroidRuntime::getJNIEnv();
    182     env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
    183 }
    184 
    185 static jint
    186 android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
    187         jint maxChannels, jobject jaa)
    188 {
    189     if (jaa == 0) {
    190         ALOGE("Error creating SoundPool: invalid audio attributes");
    191         return -1;
    192     }
    193 
    194     audio_attributes_t *paa = NULL;
    195     // read the AudioAttributes values
    196     paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
    197     const jstring jtags =
    198             (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
    199     const char* tags = env->GetStringUTFChars(jtags, NULL);
    200     // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
    201     strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
    202     env->ReleaseStringUTFChars(jtags, tags);
    203     paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
    204     paa->content_type =
    205             (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
    206     paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
    207 
    208     ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
    209     SoundPool *ap = new SoundPool(maxChannels, paa);
    210     if (ap == NULL) {
    211         return -1;
    212     }
    213 
    214     // save pointer to SoundPool C++ object in opaque field in Java object
    215     env->SetLongField(thiz, fields.mNativeContext, (jlong) ap);
    216 
    217     // set callback with weak reference
    218     jobject globalWeakRef = env->NewGlobalRef(weakRef);
    219     ap->setCallback(android_media_callback, globalWeakRef);
    220 
    221     // audio attributes were copied in SoundPool creation
    222     free(paa);
    223 
    224     return 0;
    225 }
    226 
    227 static void
    228 android_media_SoundPool_SoundPoolImpl_release(JNIEnv *env, jobject thiz)
    229 {
    230     ALOGV("android_media_SoundPool_SoundPoolImpl_release");
    231     SoundPool *ap = MusterSoundPool(env, thiz);
    232     if (ap != NULL) {
    233 
    234         // release weak reference
    235         jobject weakRef = (jobject) ap->getUserData();
    236         if (weakRef != NULL) {
    237             env->DeleteGlobalRef(weakRef);
    238         }
    239 
    240         // clear callback and native context
    241         ap->setCallback(NULL, NULL);
    242         env->SetLongField(thiz, fields.mNativeContext, 0);
    243         delete ap;
    244     }
    245 }
    246 
    247 // ----------------------------------------------------------------------------
    248 
    249 // Dalvik VM type signatures
    250 static JNINativeMethod gMethods[] = {
    251     {   "_load",
    252         "(Ljava/lang/String;I)I",
    253         (void *)android_media_SoundPool_SoundPoolImpl_load_URL
    254     },
    255     {   "_load",
    256         "(Ljava/io/FileDescriptor;JJI)I",
    257         (void *)android_media_SoundPool_SoundPoolImpl_load_FD
    258     },
    259     {   "unload",
    260         "(I)Z",
    261         (void *)android_media_SoundPool_SoundPoolImpl_unload
    262     },
    263     {   "_play",
    264         "(IFFIIF)I",
    265         (void *)android_media_SoundPool_SoundPoolImpl_play
    266     },
    267     {   "pause",
    268         "(I)V",
    269         (void *)android_media_SoundPool_SoundPoolImpl_pause
    270     },
    271     {   "resume",
    272         "(I)V",
    273         (void *)android_media_SoundPool_SoundPoolImpl_resume
    274     },
    275     {   "autoPause",
    276         "()V",
    277         (void *)android_media_SoundPool_SoundPoolImpl_autoPause
    278     },
    279     {   "autoResume",
    280         "()V",
    281         (void *)android_media_SoundPool_SoundPoolImpl_autoResume
    282     },
    283     {   "stop",
    284         "(I)V",
    285         (void *)android_media_SoundPool_SoundPoolImpl_stop
    286     },
    287     {   "_setVolume",
    288         "(IFF)V",
    289         (void *)android_media_SoundPool_SoundPoolImpl_setVolume
    290     },
    291     {   "setPriority",
    292         "(II)V",
    293         (void *)android_media_SoundPool_SoundPoolImpl_setPriority
    294     },
    295     {   "setLoop",
    296         "(II)V",
    297         (void *)android_media_SoundPool_SoundPoolImpl_setLoop
    298     },
    299     {   "setRate",
    300         "(IF)V",
    301         (void *)android_media_SoundPool_SoundPoolImpl_setRate
    302     },
    303     {   "native_setup",
    304         "(Ljava/lang/Object;ILjava/lang/Object;)I",
    305         (void*)android_media_SoundPool_SoundPoolImpl_native_setup
    306     },
    307     {   "release",
    308         "()V",
    309         (void*)android_media_SoundPool_SoundPoolImpl_release
    310     }
    311 };
    312 
    313 static const char* const kClassPathName = "android/media/SoundPool$SoundPoolImpl";
    314 
    315 jint JNI_OnLoad(JavaVM* vm, void* reserved)
    316 {
    317     JNIEnv* env = NULL;
    318     jint result = -1;
    319     jclass clazz;
    320 
    321     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    322         ALOGE("ERROR: GetEnv failed\n");
    323         return result;
    324     }
    325     assert(env != NULL);
    326 
    327     clazz = env->FindClass(kClassPathName);
    328     if (clazz == NULL) {
    329         ALOGE("Can't find %s", kClassPathName);
    330         return result;
    331     }
    332 
    333     fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
    334     if (fields.mNativeContext == NULL) {
    335         ALOGE("Can't find SoundPoolImpl.mNativeContext");
    336         return result;
    337     }
    338 
    339     fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
    340                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    341     if (fields.mPostEvent == NULL) {
    342         ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
    343         return result;
    344     }
    345 
    346     // create a reference to class. Technically, we're leaking this reference
    347     // since it's a static object.
    348     fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
    349 
    350     if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
    351         return result;
    352 
    353     // Get the AudioAttributes class and fields
    354     jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
    355     if (audioAttrClass == NULL) {
    356         ALOGE("Can't find %s", kAudioAttributesClassPathName);
    357         return result;
    358     }
    359     jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
    360     javaAudioAttrFields.fieldUsage = env->GetFieldID(audioAttributesClassRef, "mUsage", "I");
    361     javaAudioAttrFields.fieldContentType
    362                                    = env->GetFieldID(audioAttributesClassRef, "mContentType", "I");
    363     javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
    364     javaAudioAttrFields.fieldFormattedTags =
    365             env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
    366     env->DeleteGlobalRef(audioAttributesClassRef);
    367     if (javaAudioAttrFields.fieldUsage == NULL || javaAudioAttrFields.fieldContentType == NULL
    368             || javaAudioAttrFields.fieldFlags == NULL
    369             || javaAudioAttrFields.fieldFormattedTags == NULL) {
    370         ALOGE("Can't initialize AudioAttributes fields");
    371         return result;
    372     }
    373 
    374     /* success -- return valid version number */
    375     return JNI_VERSION_1_4;
    376 }
    377