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