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