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