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 //#define LOG_NDEBUG 0 17 18 #define LOG_TAG "AudioTrack-JNI" 19 20 #include "android_media_AudioTrack.h" 21 22 #include <JNIHelp.h> 23 #include <JniConstants.h> 24 #include "core_jni_helpers.h" 25 26 #include "ScopedBytes.h" 27 28 #include <utils/Log.h> 29 #include <media/AudioSystem.h> 30 #include <media/AudioTrack.h> 31 #include <audio_utils/primitives.h> 32 33 #include <binder/MemoryHeapBase.h> 34 #include <binder/MemoryBase.h> 35 36 #include "android_media_AudioFormat.h" 37 #include "android_media_AudioErrors.h" 38 #include "android_media_PlaybackParams.h" 39 #include "android_media_DeviceCallback.h" 40 41 // ---------------------------------------------------------------------------- 42 43 using namespace android; 44 45 // ---------------------------------------------------------------------------- 46 static const char* const kClassPathName = "android/media/AudioTrack"; 47 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; 48 49 struct audio_track_fields_t { 50 // these fields provide access from C++ to the... 51 jmethodID postNativeEventInJava; //... event post callback method 52 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object 53 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack 54 jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object 55 }; 56 struct audio_attributes_fields_t { 57 jfieldID fieldUsage; // AudioAttributes.mUsage 58 jfieldID fieldContentType; // AudioAttributes.mContentType 59 jfieldID fieldFlags; // AudioAttributes.mFlags 60 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags 61 }; 62 static audio_track_fields_t javaAudioTrackFields; 63 static audio_attributes_fields_t javaAudioAttrFields; 64 static PlaybackParams::fields_t gPlaybackParamsFields; 65 66 struct audiotrack_callback_cookie { 67 jclass audioTrack_class; 68 jobject audioTrack_ref; 69 bool busy; 70 Condition cond; 71 }; 72 73 // keep these values in sync with AudioTrack.java 74 #define MODE_STATIC 0 75 #define MODE_STREAM 1 76 77 // ---------------------------------------------------------------------------- 78 class AudioTrackJniStorage { 79 public: 80 sp<MemoryHeapBase> mMemHeap; 81 sp<MemoryBase> mMemBase; 82 audiotrack_callback_cookie mCallbackData; 83 sp<JNIDeviceCallback> mDeviceCallback; 84 85 AudioTrackJniStorage() { 86 mCallbackData.audioTrack_class = 0; 87 mCallbackData.audioTrack_ref = 0; 88 } 89 90 ~AudioTrackJniStorage() { 91 mMemBase.clear(); 92 mMemHeap.clear(); 93 } 94 95 bool allocSharedMem(int sizeInBytes) { 96 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); 97 if (mMemHeap->getHeapID() < 0) { 98 return false; 99 } 100 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes); 101 return true; 102 } 103 }; 104 105 static Mutex sLock; 106 static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; 107 108 // ---------------------------------------------------------------------------- 109 #define DEFAULT_OUTPUT_SAMPLE_RATE 44100 110 111 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -16 112 #define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK -17 113 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -18 114 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -19 115 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -20 116 117 // ---------------------------------------------------------------------------- 118 static void audioCallback(int event, void* user, void *info) { 119 120 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user; 121 { 122 Mutex::Autolock l(sLock); 123 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) { 124 return; 125 } 126 callbackInfo->busy = true; 127 } 128 129 switch (event) { 130 case AudioTrack::EVENT_MARKER: { 131 JNIEnv *env = AndroidRuntime::getJNIEnv(); 132 if (user != NULL && env != NULL) { 133 env->CallStaticVoidMethod( 134 callbackInfo->audioTrack_class, 135 javaAudioTrackFields.postNativeEventInJava, 136 callbackInfo->audioTrack_ref, event, 0,0, NULL); 137 if (env->ExceptionCheck()) { 138 env->ExceptionDescribe(); 139 env->ExceptionClear(); 140 } 141 } 142 } break; 143 144 case AudioTrack::EVENT_NEW_POS: { 145 JNIEnv *env = AndroidRuntime::getJNIEnv(); 146 if (user != NULL && env != NULL) { 147 env->CallStaticVoidMethod( 148 callbackInfo->audioTrack_class, 149 javaAudioTrackFields.postNativeEventInJava, 150 callbackInfo->audioTrack_ref, event, 0,0, NULL); 151 if (env->ExceptionCheck()) { 152 env->ExceptionDescribe(); 153 env->ExceptionClear(); 154 } 155 } 156 } break; 157 } 158 159 { 160 Mutex::Autolock l(sLock); 161 callbackInfo->busy = false; 162 callbackInfo->cond.broadcast(); 163 } 164 } 165 166 167 // ---------------------------------------------------------------------------- 168 static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) 169 { 170 Mutex::Autolock l(sLock); 171 AudioTrack* const at = 172 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); 173 return sp<AudioTrack>(at); 174 } 175 176 static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at) 177 { 178 Mutex::Autolock l(sLock); 179 sp<AudioTrack> old = 180 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); 181 if (at.get()) { 182 at->incStrong((void*)setAudioTrack); 183 } 184 if (old != 0) { 185 old->decStrong((void*)setAudioTrack); 186 } 187 env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get()); 188 return old; 189 } 190 191 // ---------------------------------------------------------------------------- 192 sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) { 193 return getAudioTrack(env, audioTrackObj); 194 } 195 196 // This function converts Java channel masks to a native channel mask. 197 // validity should be checked with audio_is_output_channel(). 198 static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks( 199 jint channelPositionMask, jint channelIndexMask) 200 { 201 if (channelIndexMask != 0) { // channel index mask takes priority 202 // To convert to a native channel mask, the Java channel index mask 203 // requires adding the index representation. 204 return audio_channel_mask_from_representation_and_bits( 205 AUDIO_CHANNEL_REPRESENTATION_INDEX, 206 channelIndexMask); 207 } 208 // To convert to a native channel mask, the Java channel position mask 209 // requires a shift by 2 to skip the two deprecated channel 210 // configurations "default" and "mono". 211 return (audio_channel_mask_t)(channelPositionMask >> 2); 212 } 213 214 // ---------------------------------------------------------------------------- 215 static jint 216 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, 217 jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask, 218 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, 219 jlong nativeAudioTrack) { 220 221 ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d" 222 "nativeAudioTrack=0x%llX", 223 jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, 224 nativeAudioTrack); 225 226 sp<AudioTrack> lpTrack = 0; 227 228 if (jSession == NULL) { 229 ALOGE("Error creating AudioTrack: invalid session ID pointer"); 230 return (jint) AUDIO_JAVA_ERROR; 231 } 232 233 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 234 if (nSession == NULL) { 235 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 236 return (jint) AUDIO_JAVA_ERROR; 237 } 238 audio_session_t sessionId = (audio_session_t) nSession[0]; 239 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 240 nSession = NULL; 241 242 AudioTrackJniStorage* lpJniStorage = NULL; 243 244 audio_attributes_t *paa = NULL; 245 246 jclass clazz = env->GetObjectClass(thiz); 247 if (clazz == NULL) { 248 ALOGE("Can't find %s when setting up callback.", kClassPathName); 249 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 250 } 251 252 // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one. 253 if (nativeAudioTrack == 0) { 254 if (jaa == 0) { 255 ALOGE("Error creating AudioTrack: invalid audio attributes"); 256 return (jint) AUDIO_JAVA_ERROR; 257 } 258 259 if (jSampleRate == 0) { 260 ALOGE("Error creating AudioTrack: invalid sample rates"); 261 return (jint) AUDIO_JAVA_ERROR; 262 } 263 264 int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL); 265 int sampleRateInHertz = sampleRates[0]; 266 env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT); 267 268 // Invalid channel representations are caught by !audio_is_output_channel() below. 269 audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks( 270 channelPositionMask, channelIndexMask); 271 if (!audio_is_output_channel(nativeChannelMask)) { 272 ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask); 273 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK; 274 } 275 276 uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask); 277 278 // check the format. 279 // This function was called from Java, so we compare the format against the Java constants 280 audio_format_t format = audioFormatToNative(audioFormat); 281 if (format == AUDIO_FORMAT_INVALID) { 282 ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat); 283 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; 284 } 285 286 // compute the frame count 287 size_t frameCount; 288 if (audio_is_linear_pcm(format)) { 289 const size_t bytesPerSample = audio_bytes_per_sample(format); 290 frameCount = buffSizeInBytes / (channelCount * bytesPerSample); 291 } else { 292 frameCount = buffSizeInBytes; 293 } 294 295 // create the native AudioTrack object 296 lpTrack = new AudioTrack(); 297 298 // read the AudioAttributes values 299 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); 300 const jstring jtags = 301 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); 302 const char* tags = env->GetStringUTFChars(jtags, NULL); 303 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it 304 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); 305 env->ReleaseStringUTFChars(jtags, tags); 306 paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage); 307 paa->content_type = 308 (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType); 309 paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); 310 311 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s", 312 paa->usage, paa->content_type, paa->flags, paa->tags); 313 314 // initialize the callback information: 315 // this data will be passed with every AudioTrack callback 316 lpJniStorage = new AudioTrackJniStorage(); 317 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); 318 // we use a weak reference so the AudioTrack object can be garbage collected. 319 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); 320 lpJniStorage->mCallbackData.busy = false; 321 322 // initialize the native AudioTrack object 323 status_t status = NO_ERROR; 324 switch (memoryMode) { 325 case MODE_STREAM: 326 327 status = lpTrack->set( 328 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) 329 sampleRateInHertz, 330 format,// word length, PCM 331 nativeChannelMask, 332 frameCount, 333 AUDIO_OUTPUT_FLAG_NONE, 334 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 335 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 336 0,// shared mem 337 true,// thread can call Java 338 sessionId,// audio session ID 339 AudioTrack::TRANSFER_SYNC, 340 NULL, // default offloadInfo 341 -1, -1, // default uid, pid values 342 paa); 343 break; 344 345 case MODE_STATIC: 346 // AudioTrack is using shared memory 347 348 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { 349 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); 350 goto native_init_failure; 351 } 352 353 status = lpTrack->set( 354 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) 355 sampleRateInHertz, 356 format,// word length, PCM 357 nativeChannelMask, 358 frameCount, 359 AUDIO_OUTPUT_FLAG_NONE, 360 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); 361 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 362 lpJniStorage->mMemBase,// shared mem 363 true,// thread can call Java 364 sessionId,// audio session ID 365 AudioTrack::TRANSFER_SHARED, 366 NULL, // default offloadInfo 367 -1, -1, // default uid, pid values 368 paa); 369 break; 370 371 default: 372 ALOGE("Unknown mode %d", memoryMode); 373 goto native_init_failure; 374 } 375 376 if (status != NO_ERROR) { 377 ALOGE("Error %d initializing AudioTrack", status); 378 goto native_init_failure; 379 } 380 } else { // end if (nativeAudioTrack == 0) 381 lpTrack = (AudioTrack*)nativeAudioTrack; 382 // TODO: We need to find out which members of the Java AudioTrack might 383 // need to be initialized from the Native AudioTrack 384 // these are directly returned from getters: 385 // mSampleRate 386 // mAudioFormat 387 // mStreamType 388 // mChannelConfiguration 389 // mChannelCount 390 // mState (?) 391 // mPlayState (?) 392 // these may be used internally (Java AudioTrack.audioParamCheck(): 393 // mChannelMask 394 // mChannelIndexMask 395 // mDataLoadMode 396 397 // initialize the callback information: 398 // this data will be passed with every AudioTrack callback 399 lpJniStorage = new AudioTrackJniStorage(); 400 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); 401 // we use a weak reference so the AudioTrack object can be garbage collected. 402 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); 403 lpJniStorage->mCallbackData.busy = false; 404 } 405 406 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 407 if (nSession == NULL) { 408 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 409 goto native_init_failure; 410 } 411 // read the audio session ID back from AudioTrack in case we create a new session 412 nSession[0] = lpTrack->getSessionId(); 413 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 414 nSession = NULL; 415 416 { 417 const jint elements[1] = { (jint) lpTrack->getSampleRate() }; 418 env->SetIntArrayRegion(jSampleRate, 0, 1, elements); 419 } 420 421 { // scope for the lock 422 Mutex::Autolock l(sLock); 423 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData); 424 } 425 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field 426 // of the Java object (in mNativeTrackInJavaObj) 427 setAudioTrack(env, thiz, lpTrack); 428 429 // save the JNI resources so we can free them later 430 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage); 431 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage); 432 433 // since we had audio attributes, the stream type was derived from them during the 434 // creation of the native AudioTrack: push the same value to the Java object 435 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType()); 436 if (paa != NULL) { 437 // audio attributes were copied in AudioTrack creation 438 free(paa); 439 paa = NULL; 440 } 441 442 443 return (jint) AUDIO_JAVA_SUCCESS; 444 445 // failures: 446 native_init_failure: 447 if (paa != NULL) { 448 free(paa); 449 } 450 if (nSession != NULL) { 451 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 452 } 453 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class); 454 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref); 455 delete lpJniStorage; 456 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); 457 458 // lpTrack goes out of scope, so reference count drops to zero 459 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 460 } 461 462 // ---------------------------------------------------------------------------- 463 static void 464 android_media_AudioTrack_start(JNIEnv *env, jobject thiz) 465 { 466 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 467 if (lpTrack == NULL) { 468 jniThrowException(env, "java/lang/IllegalStateException", 469 "Unable to retrieve AudioTrack pointer for start()"); 470 return; 471 } 472 473 lpTrack->start(); 474 } 475 476 477 // ---------------------------------------------------------------------------- 478 static void 479 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz) 480 { 481 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 482 if (lpTrack == NULL) { 483 jniThrowException(env, "java/lang/IllegalStateException", 484 "Unable to retrieve AudioTrack pointer for stop()"); 485 return; 486 } 487 488 lpTrack->stop(); 489 } 490 491 492 // ---------------------------------------------------------------------------- 493 static void 494 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz) 495 { 496 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 497 if (lpTrack == NULL) { 498 jniThrowException(env, "java/lang/IllegalStateException", 499 "Unable to retrieve AudioTrack pointer for pause()"); 500 return; 501 } 502 503 lpTrack->pause(); 504 } 505 506 507 // ---------------------------------------------------------------------------- 508 static void 509 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz) 510 { 511 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 512 if (lpTrack == NULL) { 513 jniThrowException(env, "java/lang/IllegalStateException", 514 "Unable to retrieve AudioTrack pointer for flush()"); 515 return; 516 } 517 518 lpTrack->flush(); 519 } 520 521 // ---------------------------------------------------------------------------- 522 static void 523 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol ) 524 { 525 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 526 if (lpTrack == NULL) { 527 jniThrowException(env, "java/lang/IllegalStateException", 528 "Unable to retrieve AudioTrack pointer for setVolume()"); 529 return; 530 } 531 532 lpTrack->setVolume(leftVol, rightVol); 533 } 534 535 // ---------------------------------------------------------------------------- 536 537 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 538 static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) { 539 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0); 540 if (lpTrack == NULL) { 541 return; 542 } 543 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack); 544 545 // delete the JNI data 546 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( 547 thiz, javaAudioTrackFields.jniData); 548 // reset the native resources in the Java object so any attempt to access 549 // them after a call to release fails. 550 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); 551 552 if (pJniStorage) { 553 Mutex::Autolock l(sLock); 554 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData; 555 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage); 556 while (lpCookie->busy) { 557 if (lpCookie->cond.waitRelative(sLock, 558 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 559 NO_ERROR) { 560 break; 561 } 562 } 563 sAudioTrackCallBackCookies.remove(lpCookie); 564 // delete global refs created in native_setup 565 env->DeleteGlobalRef(lpCookie->audioTrack_class); 566 env->DeleteGlobalRef(lpCookie->audioTrack_ref); 567 delete pJniStorage; 568 } 569 } 570 571 572 // ---------------------------------------------------------------------------- 573 static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) { 574 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz); 575 android_media_AudioTrack_release(env, thiz); 576 } 577 578 // overloaded JNI array helper functions (same as in android_media_AudioRecord) 579 static inline 580 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) { 581 return env->GetByteArrayElements(array, isCopy); 582 } 583 584 static inline 585 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) { 586 env->ReleaseByteArrayElements(array, elems, mode); 587 } 588 589 static inline 590 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) { 591 return env->GetShortArrayElements(array, isCopy); 592 } 593 594 static inline 595 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) { 596 env->ReleaseShortArrayElements(array, elems, mode); 597 } 598 599 static inline 600 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) { 601 return env->GetFloatArrayElements(array, isCopy); 602 } 603 604 static inline 605 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) { 606 env->ReleaseFloatArrayElements(array, elems, mode); 607 } 608 609 static inline 610 jint interpretWriteSizeError(ssize_t writeSize) { 611 if (writeSize == WOULD_BLOCK) { 612 return (jint)0; 613 } else if (writeSize == NO_INIT) { 614 return AUDIO_JAVA_DEAD_OBJECT; 615 } else { 616 ALOGE("Error %zd during AudioTrack native read", writeSize); 617 return nativeToJavaStatus(writeSize); 618 } 619 } 620 621 // ---------------------------------------------------------------------------- 622 template <typename T> 623 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data, 624 jint offsetInSamples, jint sizeInSamples, bool blocking) { 625 // give the data to the native AudioTrack object (the data starts at the offset) 626 ssize_t written = 0; 627 // regular write() or copy the data to the AudioTrack's shared memory? 628 size_t sizeInBytes = sizeInSamples * sizeof(T); 629 if (track->sharedBuffer() == 0) { 630 written = track->write(data + offsetInSamples, sizeInBytes, blocking); 631 // for compatibility with earlier behavior of write(), return 0 in this case 632 if (written == (ssize_t) WOULD_BLOCK) { 633 written = 0; 634 } 635 } else { 636 // writing to shared memory, check for capacity 637 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { 638 sizeInBytes = track->sharedBuffer()->size(); 639 } 640 memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes); 641 written = sizeInBytes; 642 } 643 if (written >= 0) { 644 return written / sizeof(T); 645 } 646 return interpretWriteSizeError(written); 647 } 648 649 // ---------------------------------------------------------------------------- 650 template <typename T> 651 static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz, 652 T javaAudioData, 653 jint offsetInSamples, jint sizeInSamples, 654 jint javaAudioFormat, 655 jboolean isWriteBlocking) { 656 //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called", 657 // offsetInSamples, sizeInSamples); 658 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 659 if (lpTrack == NULL) { 660 jniThrowException(env, "java/lang/IllegalStateException", 661 "Unable to retrieve AudioTrack pointer for write()"); 662 return (jint)AUDIO_JAVA_INVALID_OPERATION; 663 } 664 665 if (javaAudioData == NULL) { 666 ALOGE("NULL java array of audio data to play"); 667 return (jint)AUDIO_JAVA_BAD_VALUE; 668 } 669 670 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 671 // a way that it becomes much more efficient. When doing so, we will have to prevent the 672 // AudioSystem callback to be called while in critical section (in case of media server 673 // process crash for instance) 674 675 // get the pointer for the audio data from the java array 676 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL); 677 if (cAudioData == NULL) { 678 ALOGE("Error retrieving source of audio data to play"); 679 return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load 680 } 681 682 jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData, 683 offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */); 684 685 envReleaseArrayElements(env, javaAudioData, cAudioData, 0); 686 687 //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d", 688 // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples); 689 return samplesWritten; 690 } 691 692 // ---------------------------------------------------------------------------- 693 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz, 694 jbyteArray javaBytes, jint byteOffset, jint sizeInBytes, 695 jint javaAudioFormat, jboolean isWriteBlocking) { 696 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called", 697 // offsetInBytes, sizeInBytes); 698 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 699 if (lpTrack == NULL) { 700 jniThrowException(env, "java/lang/IllegalStateException", 701 "Unable to retrieve AudioTrack pointer for write()"); 702 return (jint)AUDIO_JAVA_INVALID_OPERATION; 703 } 704 705 ScopedBytesRO bytes(env, javaBytes); 706 if (bytes.get() == NULL) { 707 ALOGE("Error retrieving source of audio data to play, can't play"); 708 return (jint)AUDIO_JAVA_BAD_VALUE; 709 } 710 711 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset, 712 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); 713 714 return written; 715 } 716 717 // ---------------------------------------------------------------------------- 718 static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env, jobject thiz) { 719 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 720 if (lpTrack == NULL) { 721 jniThrowException(env, "java/lang/IllegalStateException", 722 "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()"); 723 return (jint)AUDIO_JAVA_ERROR; 724 } 725 726 ssize_t result = lpTrack->getBufferSizeInFrames(); 727 if (result < 0) { 728 jniThrowException(env, "java/lang/IllegalStateException", 729 "Internal error detected in getBufferSizeInFrames() = " + result); 730 return (jint)AUDIO_JAVA_ERROR; 731 } 732 return (jint)result; 733 } 734 735 // ---------------------------------------------------------------------------- 736 static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env, 737 jobject thiz, jint bufferSizeInFrames) { 738 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 739 if (lpTrack == NULL) { 740 jniThrowException(env, "java/lang/IllegalStateException", 741 "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()"); 742 return (jint)AUDIO_JAVA_ERROR; 743 } 744 // Value will be coerced into the valid range. 745 // But internal values are unsigned, size_t, so we need to clip 746 // against zero here where it is signed. 747 if (bufferSizeInFrames < 0) { 748 bufferSizeInFrames = 0; 749 } 750 ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames); 751 if (result < 0) { 752 jniThrowException(env, "java/lang/IllegalStateException", 753 "Internal error detected in setBufferSizeInFrames() = " + result); 754 return (jint)AUDIO_JAVA_ERROR; 755 } 756 return (jint)result; 757 } 758 759 // ---------------------------------------------------------------------------- 760 static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env, jobject thiz) { 761 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 762 if (lpTrack == NULL) { 763 jniThrowException(env, "java/lang/IllegalStateException", 764 "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()"); 765 return (jint)AUDIO_JAVA_ERROR; 766 } 767 768 return lpTrack->frameCount(); 769 } 770 771 // ---------------------------------------------------------------------------- 772 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, 773 jint sampleRateInHz) { 774 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 775 if (lpTrack == NULL) { 776 jniThrowException(env, "java/lang/IllegalStateException", 777 "Unable to retrieve AudioTrack pointer for setSampleRate()"); 778 return (jint)AUDIO_JAVA_ERROR; 779 } 780 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz)); 781 } 782 783 784 // ---------------------------------------------------------------------------- 785 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) { 786 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 787 if (lpTrack == NULL) { 788 jniThrowException(env, "java/lang/IllegalStateException", 789 "Unable to retrieve AudioTrack pointer for getSampleRate()"); 790 return (jint)AUDIO_JAVA_ERROR; 791 } 792 return (jint) lpTrack->getSampleRate(); 793 } 794 795 796 // ---------------------------------------------------------------------------- 797 static void android_media_AudioTrack_set_playback_params(JNIEnv *env, jobject thiz, 798 jobject params) { 799 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 800 if (lpTrack == NULL) { 801 jniThrowException(env, "java/lang/IllegalStateException", 802 "AudioTrack not initialized"); 803 return; 804 } 805 806 PlaybackParams pbp; 807 pbp.fillFromJobject(env, gPlaybackParamsFields, params); 808 809 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u", 810 pbp.speedSet, pbp.audioRate.mSpeed, 811 pbp.pitchSet, pbp.audioRate.mPitch, 812 pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode, 813 pbp.audioStretchModeSet, pbp.audioRate.mStretchMode); 814 815 // to simulate partially set params, we do a read-modify-write. 816 // TODO: pass in the valid set mask into AudioTrack. 817 AudioPlaybackRate rate = lpTrack->getPlaybackRate(); 818 bool updatedRate = false; 819 if (pbp.speedSet) { 820 rate.mSpeed = pbp.audioRate.mSpeed; 821 updatedRate = true; 822 } 823 if (pbp.pitchSet) { 824 rate.mPitch = pbp.audioRate.mPitch; 825 updatedRate = true; 826 } 827 if (pbp.audioFallbackModeSet) { 828 rate.mFallbackMode = pbp.audioRate.mFallbackMode; 829 updatedRate = true; 830 } 831 if (pbp.audioStretchModeSet) { 832 rate.mStretchMode = pbp.audioRate.mStretchMode; 833 updatedRate = true; 834 } 835 if (updatedRate) { 836 if (lpTrack->setPlaybackRate(rate) != OK) { 837 jniThrowException(env, "java/lang/IllegalArgumentException", 838 "arguments out of range"); 839 } 840 } 841 } 842 843 844 // ---------------------------------------------------------------------------- 845 static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env, jobject thiz, 846 jobject params) { 847 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 848 if (lpTrack == NULL) { 849 jniThrowException(env, "java/lang/IllegalStateException", 850 "AudioTrack not initialized"); 851 return NULL; 852 } 853 854 PlaybackParams pbs; 855 pbs.audioRate = lpTrack->getPlaybackRate(); 856 pbs.speedSet = true; 857 pbs.pitchSet = true; 858 pbs.audioFallbackModeSet = true; 859 pbs.audioStretchModeSet = true; 860 return pbs.asJobject(env, gPlaybackParamsFields); 861 } 862 863 864 // ---------------------------------------------------------------------------- 865 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz, 866 jint markerPos) { 867 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 868 if (lpTrack == NULL) { 869 jniThrowException(env, "java/lang/IllegalStateException", 870 "Unable to retrieve AudioTrack pointer for setMarkerPosition()"); 871 return (jint)AUDIO_JAVA_ERROR; 872 } 873 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) ); 874 } 875 876 877 // ---------------------------------------------------------------------------- 878 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) { 879 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 880 uint32_t markerPos = 0; 881 882 if (lpTrack == NULL) { 883 jniThrowException(env, "java/lang/IllegalStateException", 884 "Unable to retrieve AudioTrack pointer for getMarkerPosition()"); 885 return (jint)AUDIO_JAVA_ERROR; 886 } 887 lpTrack->getMarkerPosition(&markerPos); 888 return (jint)markerPos; 889 } 890 891 892 // ---------------------------------------------------------------------------- 893 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz, 894 jint period) { 895 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 896 if (lpTrack == NULL) { 897 jniThrowException(env, "java/lang/IllegalStateException", 898 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()"); 899 return (jint)AUDIO_JAVA_ERROR; 900 } 901 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) ); 902 } 903 904 905 // ---------------------------------------------------------------------------- 906 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) { 907 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 908 uint32_t period = 0; 909 910 if (lpTrack == NULL) { 911 jniThrowException(env, "java/lang/IllegalStateException", 912 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()"); 913 return (jint)AUDIO_JAVA_ERROR; 914 } 915 lpTrack->getPositionUpdatePeriod(&period); 916 return (jint)period; 917 } 918 919 920 // ---------------------------------------------------------------------------- 921 static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz, 922 jint position) { 923 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 924 if (lpTrack == NULL) { 925 jniThrowException(env, "java/lang/IllegalStateException", 926 "Unable to retrieve AudioTrack pointer for setPosition()"); 927 return (jint)AUDIO_JAVA_ERROR; 928 } 929 return nativeToJavaStatus( lpTrack->setPosition(position) ); 930 } 931 932 933 // ---------------------------------------------------------------------------- 934 static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) { 935 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 936 uint32_t position = 0; 937 938 if (lpTrack == NULL) { 939 jniThrowException(env, "java/lang/IllegalStateException", 940 "Unable to retrieve AudioTrack pointer for getPosition()"); 941 return (jint)AUDIO_JAVA_ERROR; 942 } 943 lpTrack->getPosition(&position); 944 return (jint)position; 945 } 946 947 948 // ---------------------------------------------------------------------------- 949 static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) { 950 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 951 952 if (lpTrack == NULL) { 953 jniThrowException(env, "java/lang/IllegalStateException", 954 "Unable to retrieve AudioTrack pointer for latency()"); 955 return (jint)AUDIO_JAVA_ERROR; 956 } 957 return (jint)lpTrack->latency(); 958 } 959 960 // ---------------------------------------------------------------------------- 961 static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject thiz) { 962 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 963 964 if (lpTrack == NULL) { 965 jniThrowException(env, "java/lang/IllegalStateException", 966 "Unable to retrieve AudioTrack pointer for getUnderrunCount()"); 967 return (jint)AUDIO_JAVA_ERROR; 968 } 969 return (jint)lpTrack->getUnderrunCount(); 970 } 971 972 // ---------------------------------------------------------------------------- 973 static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) { 974 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 975 976 if (lpTrack == NULL) { 977 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()"); 978 return (jint)AUDIO_JAVA_ERROR; 979 } 980 AudioTimestamp timestamp; 981 status_t status = lpTrack->getTimestamp(timestamp); 982 if (status == OK) { 983 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL); 984 if (nTimestamp == NULL) { 985 ALOGE("Unable to get array for getTimestamp()"); 986 return (jint)AUDIO_JAVA_ERROR; 987 } 988 nTimestamp[0] = (jlong) timestamp.mPosition; 989 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec); 990 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0); 991 } 992 return (jint) nativeToJavaStatus(status); 993 } 994 995 996 // ---------------------------------------------------------------------------- 997 static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz, 998 jint loopStart, jint loopEnd, jint loopCount) { 999 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1000 if (lpTrack == NULL) { 1001 jniThrowException(env, "java/lang/IllegalStateException", 1002 "Unable to retrieve AudioTrack pointer for setLoop()"); 1003 return (jint)AUDIO_JAVA_ERROR; 1004 } 1005 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) ); 1006 } 1007 1008 1009 // ---------------------------------------------------------------------------- 1010 static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { 1011 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1012 if (lpTrack == NULL) { 1013 jniThrowException(env, "java/lang/IllegalStateException", 1014 "Unable to retrieve AudioTrack pointer for reload()"); 1015 return (jint)AUDIO_JAVA_ERROR; 1016 } 1017 return nativeToJavaStatus( lpTrack->reload() ); 1018 } 1019 1020 1021 // ---------------------------------------------------------------------------- 1022 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz, 1023 jint javaStreamType) { 1024 uint32_t afSamplingRate; 1025 // convert the stream type from Java to native value 1026 // FIXME: code duplication with android_media_AudioTrack_setup() 1027 audio_stream_type_t nativeStreamType; 1028 switch (javaStreamType) { 1029 case AUDIO_STREAM_VOICE_CALL: 1030 case AUDIO_STREAM_SYSTEM: 1031 case AUDIO_STREAM_RING: 1032 case AUDIO_STREAM_MUSIC: 1033 case AUDIO_STREAM_ALARM: 1034 case AUDIO_STREAM_NOTIFICATION: 1035 case AUDIO_STREAM_BLUETOOTH_SCO: 1036 case AUDIO_STREAM_DTMF: 1037 nativeStreamType = (audio_stream_type_t) javaStreamType; 1038 break; 1039 default: 1040 nativeStreamType = AUDIO_STREAM_DEFAULT; 1041 break; 1042 } 1043 1044 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType); 1045 if (status != NO_ERROR) { 1046 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d " 1047 "in AudioTrack JNI", status, nativeStreamType); 1048 return DEFAULT_OUTPUT_SAMPLE_RATE; 1049 } else { 1050 return afSamplingRate; 1051 } 1052 } 1053 1054 1055 // ---------------------------------------------------------------------------- 1056 // returns the minimum required size for the successful creation of a streaming AudioTrack 1057 // returns -1 if there was an error querying the hardware. 1058 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, 1059 jint sampleRateInHertz, jint channelCount, jint audioFormat) { 1060 1061 size_t frameCount; 1062 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT, 1063 sampleRateInHertz); 1064 if (status != NO_ERROR) { 1065 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d", 1066 sampleRateInHertz, status); 1067 return -1; 1068 } 1069 const audio_format_t format = audioFormatToNative(audioFormat); 1070 if (audio_has_proportional_frames(format)) { 1071 const size_t bytesPerSample = audio_bytes_per_sample(format); 1072 return frameCount * channelCount * bytesPerSample; 1073 } else { 1074 return frameCount; 1075 } 1076 } 1077 1078 // ---------------------------------------------------------------------------- 1079 static jint 1080 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level ) 1081 { 1082 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1083 if (lpTrack == NULL ) { 1084 jniThrowException(env, "java/lang/IllegalStateException", 1085 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()"); 1086 return -1; 1087 } 1088 1089 status_t status = lpTrack->setAuxEffectSendLevel(level); 1090 if (status != NO_ERROR) { 1091 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d", 1092 level, status); 1093 } 1094 return (jint) status; 1095 } 1096 1097 // ---------------------------------------------------------------------------- 1098 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz, 1099 jint effectId) { 1100 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1101 if (lpTrack == NULL) { 1102 jniThrowException(env, "java/lang/IllegalStateException", 1103 "Unable to retrieve AudioTrack pointer for attachAuxEffect()"); 1104 return (jint)AUDIO_JAVA_ERROR; 1105 } 1106 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) ); 1107 } 1108 1109 static jboolean android_media_AudioTrack_setOutputDevice( 1110 JNIEnv *env, jobject thiz, jint device_id) { 1111 1112 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1113 if (lpTrack == 0) { 1114 return false; 1115 } 1116 return lpTrack->setOutputDevice(device_id) == NO_ERROR; 1117 } 1118 1119 static jint android_media_AudioTrack_getRoutedDeviceId( 1120 JNIEnv *env, jobject thiz) { 1121 1122 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1123 if (lpTrack == NULL) { 1124 return 0; 1125 } 1126 return (jint)lpTrack->getRoutedDeviceId(); 1127 } 1128 1129 static void android_media_AudioTrack_enableDeviceCallback( 1130 JNIEnv *env, jobject thiz) { 1131 1132 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1133 if (lpTrack == NULL) { 1134 return; 1135 } 1136 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( 1137 thiz, javaAudioTrackFields.jniData); 1138 if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) { 1139 return; 1140 } 1141 pJniStorage->mDeviceCallback = 1142 new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref, 1143 javaAudioTrackFields.postNativeEventInJava); 1144 lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback); 1145 } 1146 1147 static void android_media_AudioTrack_disableDeviceCallback( 1148 JNIEnv *env, jobject thiz) { 1149 1150 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 1151 if (lpTrack == NULL) { 1152 return; 1153 } 1154 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( 1155 thiz, javaAudioTrackFields.jniData); 1156 if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) { 1157 return; 1158 } 1159 lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback); 1160 pJniStorage->mDeviceCallback.clear(); 1161 } 1162 1163 static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) { 1164 return FCC_8; 1165 } 1166 1167 1168 // ---------------------------------------------------------------------------- 1169 // ---------------------------------------------------------------------------- 1170 static const JNINativeMethod gMethods[] = { 1171 // name, signature, funcPtr 1172 {"native_start", "()V", (void *)android_media_AudioTrack_start}, 1173 {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, 1174 {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, 1175 {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, 1176 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I", 1177 (void *)android_media_AudioTrack_setup}, 1178 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, 1179 {"native_release", "()V", (void *)android_media_AudioTrack_release}, 1180 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>}, 1181 {"native_write_native_bytes", 1182 "(Ljava/lang/Object;IIIZ)I", 1183 (void *)android_media_AudioTrack_write_native_bytes}, 1184 {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>}, 1185 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>}, 1186 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, 1187 {"native_get_buffer_size_frames", 1188 "()I", (void *)android_media_AudioTrack_get_buffer_size_frames}, 1189 {"native_set_buffer_size_frames", 1190 "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames}, 1191 {"native_get_buffer_capacity_frames", 1192 "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames}, 1193 {"native_set_playback_rate", 1194 "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, 1195 {"native_get_playback_rate", 1196 "()I", (void *)android_media_AudioTrack_get_playback_rate}, 1197 {"native_set_playback_params", 1198 "(Landroid/media/PlaybackParams;)V", 1199 (void *)android_media_AudioTrack_set_playback_params}, 1200 {"native_get_playback_params", 1201 "()Landroid/media/PlaybackParams;", 1202 (void *)android_media_AudioTrack_get_playback_params}, 1203 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, 1204 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, 1205 {"native_set_pos_update_period", 1206 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, 1207 {"native_get_pos_update_period", 1208 "()I", (void *)android_media_AudioTrack_get_pos_update_period}, 1209 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, 1210 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, 1211 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, 1212 {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count}, 1213 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, 1214 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, 1215 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, 1216 {"native_get_output_sample_rate", 1217 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate}, 1218 {"native_get_min_buff_size", 1219 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, 1220 {"native_setAuxEffectSendLevel", 1221 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel}, 1222 {"native_attachAuxEffect", 1223 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, 1224 {"native_setOutputDevice", "(I)Z", 1225 (void *)android_media_AudioTrack_setOutputDevice}, 1226 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId}, 1227 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback}, 1228 {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback}, 1229 {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8}, 1230 }; 1231 1232 1233 // field names found in android/media/AudioTrack.java 1234 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 1235 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" 1236 #define JAVA_JNIDATA_FIELD_NAME "mJniData" 1237 #define JAVA_STREAMTYPE_FIELD_NAME "mStreamType" 1238 1239 // ---------------------------------------------------------------------------- 1240 // preconditions: 1241 // theClass is valid 1242 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className, 1243 const char* constName, int* constVal) { 1244 jfieldID javaConst = NULL; 1245 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I"); 1246 if (javaConst != NULL) { 1247 *constVal = pEnv->GetStaticIntField(theClass, javaConst); 1248 return true; 1249 } else { 1250 ALOGE("Can't find %s.%s", className, constName); 1251 return false; 1252 } 1253 } 1254 1255 1256 // ---------------------------------------------------------------------------- 1257 int register_android_media_AudioTrack(JNIEnv *env) 1258 { 1259 // must be first 1260 int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); 1261 1262 javaAudioTrackFields.nativeTrackInJavaObj = NULL; 1263 javaAudioTrackFields.postNativeEventInJava = NULL; 1264 1265 // Get the AudioTrack class 1266 jclass audioTrackClass = FindClassOrDie(env, kClassPathName); 1267 1268 // Get the postEvent method 1269 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, 1270 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME, 1271 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 1272 1273 // Get the variables fields 1274 // nativeTrackInJavaObj 1275 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env, 1276 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J"); 1277 // jniData 1278 javaAudioTrackFields.jniData = GetFieldIDOrDie(env, 1279 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J"); 1280 // fieldStreamType 1281 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env, 1282 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I"); 1283 1284 env->DeleteLocalRef(audioTrackClass); 1285 1286 // Get the AudioAttributes class and fields 1287 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName); 1288 javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I"); 1289 javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env, 1290 audioAttrClass, "mContentType", "I"); 1291 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I"); 1292 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env, 1293 audioAttrClass, "mFormattedTags", "Ljava/lang/String;"); 1294 1295 env->DeleteLocalRef(audioAttrClass); 1296 1297 // initialize PlaybackParams field info 1298 gPlaybackParamsFields.init(env); 1299 1300 return res; 1301 } 1302 1303 1304 // ---------------------------------------------------------------------------- 1305