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