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