android_media_AudioTrack.cpp revision 76f6a86de25e1bf74717e047e55fd44b089673f3
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 // for the moment 8bitPCM in MODE_STATIC is not supported natively in the AudioTrack C++ class 219 // so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled 220 // in android_media_AudioTrack_native_write_byte() 221 if ((format == AUDIO_FORMAT_PCM_8_BIT) 222 && (memoryMode == MODE_STATIC)) { 223 ALOGV("android_media_AudioTrack_setup(): requesting MODE_STATIC for 8bit \ 224 buff size of %dbytes, switching to 16bit, buff size of %dbytes", 225 buffSizeInBytes, 2*buffSizeInBytes); 226 format = AUDIO_FORMAT_PCM_16_BIT; 227 // we will need twice the memory to store the data 228 buffSizeInBytes *= 2; 229 } 230 231 // compute the frame count 232 size_t frameCount; 233 if (audio_is_linear_pcm(format)) { 234 const size_t bytesPerSample = audio_bytes_per_sample(format); 235 frameCount = buffSizeInBytes / (channelCount * bytesPerSample); 236 } else { 237 frameCount = buffSizeInBytes; 238 } 239 240 jclass clazz = env->GetObjectClass(thiz); 241 if (clazz == NULL) { 242 ALOGE("Can't find %s when setting up callback.", kClassPathName); 243 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 244 } 245 246 if (jSession == NULL) { 247 ALOGE("Error creating AudioTrack: invalid session ID pointer"); 248 return (jint) AUDIO_JAVA_ERROR; 249 } 250 251 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 252 if (nSession == NULL) { 253 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 254 return (jint) AUDIO_JAVA_ERROR; 255 } 256 int sessionId = nSession[0]; 257 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 258 nSession = NULL; 259 260 // create the native AudioTrack object 261 sp<AudioTrack> lpTrack = new AudioTrack(); 262 263 audio_attributes_t *paa = NULL; 264 // read the AudioAttributes values 265 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); 266 const jstring jtags = 267 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); 268 const char* tags = env->GetStringUTFChars(jtags, NULL); 269 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it 270 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); 271 env->ReleaseStringUTFChars(jtags, tags); 272 paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage); 273 paa->content_type = 274 (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType); 275 paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); 276 277 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s", 278 paa->usage, paa->content_type, paa->flags, paa->tags); 279 280 // initialize the callback information: 281 // this data will be passed with every AudioTrack callback 282 AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); 283 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); 284 // we use a weak reference so the AudioTrack object can be garbage collected. 285 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); 286 lpJniStorage->mCallbackData.busy = false; 287 288 // initialize the native AudioTrack object 289 status_t status = NO_ERROR; 290 switch (memoryMode) { 291 case MODE_STREAM: 292 293 status = lpTrack->set( 294 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) 295 sampleRateInHertz, 296 format,// word length, PCM 297 nativeChannelMask, 298 frameCount, 299 AUDIO_OUTPUT_FLAG_NONE, 300 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 301 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 302 0,// shared mem 303 true,// thread can call Java 304 sessionId,// audio session ID 305 AudioTrack::TRANSFER_SYNC, 306 NULL, // default offloadInfo 307 -1, -1, // default uid, pid values 308 paa); 309 break; 310 311 case MODE_STATIC: 312 // AudioTrack is using shared memory 313 314 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { 315 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); 316 goto native_init_failure; 317 } 318 319 status = lpTrack->set( 320 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) 321 sampleRateInHertz, 322 format,// word length, PCM 323 nativeChannelMask, 324 frameCount, 325 AUDIO_OUTPUT_FLAG_NONE, 326 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); 327 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 328 lpJniStorage->mMemBase,// shared mem 329 true,// thread can call Java 330 sessionId,// audio session ID 331 AudioTrack::TRANSFER_SHARED, 332 NULL, // default offloadInfo 333 -1, -1, // default uid, pid values 334 paa); 335 break; 336 337 default: 338 ALOGE("Unknown mode %d", memoryMode); 339 goto native_init_failure; 340 } 341 342 if (status != NO_ERROR) { 343 ALOGE("Error %d initializing AudioTrack", status); 344 goto native_init_failure; 345 } 346 347 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 348 if (nSession == NULL) { 349 ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); 350 goto native_init_failure; 351 } 352 // read the audio session ID back from AudioTrack in case we create a new session 353 nSession[0] = lpTrack->getSessionId(); 354 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 355 nSession = NULL; 356 357 { // scope for the lock 358 Mutex::Autolock l(sLock); 359 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData); 360 } 361 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field 362 // of the Java object (in mNativeTrackInJavaObj) 363 setAudioTrack(env, thiz, lpTrack); 364 365 // save the JNI resources so we can free them later 366 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage); 367 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage); 368 369 // since we had audio attributes, the stream type was derived from them during the 370 // creation of the native AudioTrack: push the same value to the Java object 371 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType()); 372 // audio attributes were copied in AudioTrack creation 373 free(paa); 374 paa = NULL; 375 376 377 return (jint) AUDIO_JAVA_SUCCESS; 378 379 // failures: 380native_init_failure: 381 if (paa != NULL) { 382 free(paa); 383 } 384 if (nSession != NULL) { 385 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 386 } 387 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class); 388 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref); 389 delete lpJniStorage; 390 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); 391 392 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 393} 394 395 396// ---------------------------------------------------------------------------- 397static void 398android_media_AudioTrack_start(JNIEnv *env, jobject thiz) 399{ 400 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 401 if (lpTrack == NULL) { 402 jniThrowException(env, "java/lang/IllegalStateException", 403 "Unable to retrieve AudioTrack pointer for start()"); 404 return; 405 } 406 407 lpTrack->start(); 408} 409 410 411// ---------------------------------------------------------------------------- 412static void 413android_media_AudioTrack_stop(JNIEnv *env, jobject thiz) 414{ 415 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 416 if (lpTrack == NULL) { 417 jniThrowException(env, "java/lang/IllegalStateException", 418 "Unable to retrieve AudioTrack pointer for stop()"); 419 return; 420 } 421 422 lpTrack->stop(); 423} 424 425 426// ---------------------------------------------------------------------------- 427static void 428android_media_AudioTrack_pause(JNIEnv *env, jobject thiz) 429{ 430 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 431 if (lpTrack == NULL) { 432 jniThrowException(env, "java/lang/IllegalStateException", 433 "Unable to retrieve AudioTrack pointer for pause()"); 434 return; 435 } 436 437 lpTrack->pause(); 438} 439 440 441// ---------------------------------------------------------------------------- 442static void 443android_media_AudioTrack_flush(JNIEnv *env, jobject thiz) 444{ 445 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 446 if (lpTrack == NULL) { 447 jniThrowException(env, "java/lang/IllegalStateException", 448 "Unable to retrieve AudioTrack pointer for flush()"); 449 return; 450 } 451 452 lpTrack->flush(); 453} 454 455// ---------------------------------------------------------------------------- 456static void 457android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol ) 458{ 459 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 460 if (lpTrack == NULL) { 461 jniThrowException(env, "java/lang/IllegalStateException", 462 "Unable to retrieve AudioTrack pointer for setVolume()"); 463 return; 464 } 465 466 lpTrack->setVolume(leftVol, rightVol); 467} 468 469// ---------------------------------------------------------------------------- 470 471#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 472static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) { 473 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0); 474 if (lpTrack == NULL) { 475 return; 476 } 477 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack); 478 lpTrack->stop(); 479 480 // delete the JNI data 481 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField( 482 thiz, javaAudioTrackFields.jniData); 483 // reset the native resources in the Java object so any attempt to access 484 // them after a call to release fails. 485 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0); 486 487 if (pJniStorage) { 488 Mutex::Autolock l(sLock); 489 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData; 490 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage); 491 while (lpCookie->busy) { 492 if (lpCookie->cond.waitRelative(sLock, 493 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 494 NO_ERROR) { 495 break; 496 } 497 } 498 sAudioTrackCallBackCookies.remove(lpCookie); 499 // delete global refs created in native_setup 500 env->DeleteGlobalRef(lpCookie->audioTrack_class); 501 env->DeleteGlobalRef(lpCookie->audioTrack_ref); 502 delete pJniStorage; 503 } 504} 505 506 507// ---------------------------------------------------------------------------- 508static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) { 509 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz); 510 android_media_AudioTrack_release(env, thiz); 511} 512 513// ---------------------------------------------------------------------------- 514jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data, 515 jint offsetInBytes, jint sizeInBytes, bool blocking = true) { 516 // give the data to the native AudioTrack object (the data starts at the offset) 517 ssize_t written = 0; 518 // regular write() or copy the data to the AudioTrack's shared memory? 519 if (track->sharedBuffer() == 0) { 520 written = track->write(data + offsetInBytes, sizeInBytes, blocking); 521 // for compatibility with earlier behavior of write(), return 0 in this case 522 if (written == (ssize_t) WOULD_BLOCK) { 523 written = 0; 524 } 525 } else { 526 const audio_format_t format = audioFormatToNative(audioFormat); 527 switch (format) { 528 529 default: 530 case AUDIO_FORMAT_PCM_FLOAT: 531 case AUDIO_FORMAT_PCM_16_BIT: { 532 // writing to shared memory, check for capacity 533 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { 534 sizeInBytes = track->sharedBuffer()->size(); 535 } 536 memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes); 537 written = sizeInBytes; 538 } break; 539 540 case AUDIO_FORMAT_PCM_8_BIT: { 541 // data contains 8bit data we need to expand to 16bit before copying 542 // to the shared memory 543 // writing to shared memory, check for capacity, 544 // note that input data will occupy 2X the input space due to 8 to 16bit conversion 545 if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) { 546 sizeInBytes = track->sharedBuffer()->size() / 2; 547 } 548 int count = sizeInBytes; 549 int16_t *dst = (int16_t *)track->sharedBuffer()->pointer(); 550 const uint8_t *src = (const uint8_t *)(data + offsetInBytes); 551 memcpy_to_i16_from_u8(dst, src, count); 552 // even though we wrote 2*sizeInBytes, we only report sizeInBytes as written to hide 553 // the 8bit mixer restriction from the user of this function 554 written = sizeInBytes; 555 } break; 556 557 } 558 } 559 return written; 560 561} 562 563// ---------------------------------------------------------------------------- 564static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz, 565 jbyteArray javaAudioData, 566 jint offsetInBytes, jint sizeInBytes, 567 jint javaAudioFormat, 568 jboolean isWriteBlocking) { 569 //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called", 570 // offsetInBytes, sizeInBytes); 571 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 572 if (lpTrack == NULL) { 573 jniThrowException(env, "java/lang/IllegalStateException", 574 "Unable to retrieve AudioTrack pointer for write()"); 575 return 0; 576 } 577 578 // get the pointer for the audio data from the java array 579 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 580 // a way that it becomes much more efficient. When doing so, we will have to prevent the 581 // AudioSystem callback to be called while in critical section (in case of media server 582 // process crash for instance) 583 jbyte* cAudioData = NULL; 584 if (javaAudioData) { 585 cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); 586 if (cAudioData == NULL) { 587 ALOGE("Error retrieving source of audio data to play, can't play"); 588 return 0; // out of memory or no data to load 589 } 590 } else { 591 ALOGE("NULL java array of audio data to play, can't play"); 592 return 0; 593 } 594 595 jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes, 596 isWriteBlocking == JNI_TRUE /* blocking */); 597 598 env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); 599 600 //ALOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", 601 // (int)written, (int)(sizeInBytes), (int)offsetInBytes); 602 return written; 603} 604 605 606// ---------------------------------------------------------------------------- 607static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz, 608 jbyteArray javaBytes, jint byteOffset, jint sizeInBytes, 609 jint javaAudioFormat, jboolean isWriteBlocking) { 610 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called", 611 // offsetInBytes, sizeInBytes); 612 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 613 if (lpTrack == NULL) { 614 jniThrowException(env, "java/lang/IllegalStateException", 615 "Unable to retrieve AudioTrack pointer for write()"); 616 return 0; 617 } 618 619 ScopedBytesRO bytes(env, javaBytes); 620 if (bytes.get() == NULL) { 621 ALOGE("Error retrieving source of audio data to play, can't play"); 622 return (jint)AUDIO_JAVA_BAD_VALUE; 623 } 624 625 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset, 626 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */); 627 628 return written; 629} 630 631// ---------------------------------------------------------------------------- 632static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz, 633 jshortArray javaAudioData, 634 jint offsetInShorts, jint sizeInShorts, 635 jint javaAudioFormat) { 636 637 //ALOGV("android_media_AudioTrack_write_short(offset=%d, sizeInShorts=%d) called", 638 // offsetInShorts, sizeInShorts); 639 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 640 if (lpTrack == NULL) { 641 jniThrowException(env, "java/lang/IllegalStateException", 642 "Unable to retrieve AudioTrack pointer for write()"); 643 return 0; 644 } 645 646 // get the pointer for the audio data from the java array 647 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 648 // a way that it becomes much more efficient. When doing so, we will have to prevent the 649 // AudioSystem callback to be called while in critical section (in case of media server 650 // process crash for instance) 651 jshort* cAudioData = NULL; 652 if (javaAudioData) { 653 cAudioData = (jshort *)env->GetShortArrayElements(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 offsetInShorts * sizeof(short), sizeInShorts * sizeof(short), 664 true /*blocking write, legacy behavior*/); 665 env->ReleaseShortArrayElements(javaAudioData, cAudioData, 0); 666 667 if (written > 0) { 668 written /= sizeof(short); 669 } 670 //ALOGV("write wrote %d (tried %d) shorts in the native AudioTrack with offset %d", 671 // (int)written, (int)(sizeInShorts), (int)offsetInShorts); 672 673 return written; 674} 675 676 677// ---------------------------------------------------------------------------- 678static jint android_media_AudioTrack_write_float(JNIEnv *env, jobject thiz, 679 jfloatArray javaAudioData, 680 jint offsetInFloats, jint sizeInFloats, 681 jint javaAudioFormat, 682 jboolean isWriteBlocking) { 683 684 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 685 if (lpTrack == NULL) { 686 jniThrowException(env, "java/lang/IllegalStateException", 687 "Unable to retrieve AudioTrack pointer for write()"); 688 return 0; 689 } 690 691 jfloat* cAudioData = NULL; 692 if (javaAudioData) { 693 cAudioData = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL); 694 if (cAudioData == NULL) { 695 ALOGE("Error retrieving source of audio data to play, can't play"); 696 return 0; // out of memory or no data to load 697 } 698 } else { 699 ALOGE("NULL java array of audio data to play, can't play"); 700 return 0; 701 } 702 jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData, 703 offsetInFloats * sizeof(float), sizeInFloats * sizeof(float), 704 isWriteBlocking == JNI_TRUE /* blocking */); 705 env->ReleaseFloatArrayElements(javaAudioData, cAudioData, 0); 706 707 if (written > 0) { 708 written /= sizeof(float); 709 } 710 711 return written; 712} 713 714 715// ---------------------------------------------------------------------------- 716static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) { 717 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 718 if (lpTrack == NULL) { 719 jniThrowException(env, "java/lang/IllegalStateException", 720 "Unable to retrieve AudioTrack pointer for frameCount()"); 721 return (jint)AUDIO_JAVA_ERROR; 722 } 723 724 return lpTrack->frameCount(); 725} 726 727 728// ---------------------------------------------------------------------------- 729static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, 730 jint sampleRateInHz) { 731 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 732 if (lpTrack == NULL) { 733 jniThrowException(env, "java/lang/IllegalStateException", 734 "Unable to retrieve AudioTrack pointer for setSampleRate()"); 735 return (jint)AUDIO_JAVA_ERROR; 736 } 737 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz)); 738} 739 740 741// ---------------------------------------------------------------------------- 742static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) { 743 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 744 if (lpTrack == NULL) { 745 jniThrowException(env, "java/lang/IllegalStateException", 746 "Unable to retrieve AudioTrack pointer for getSampleRate()"); 747 return (jint)AUDIO_JAVA_ERROR; 748 } 749 return (jint) lpTrack->getSampleRate(); 750} 751 752 753// ---------------------------------------------------------------------------- 754static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz, 755 jint markerPos) { 756 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 757 if (lpTrack == NULL) { 758 jniThrowException(env, "java/lang/IllegalStateException", 759 "Unable to retrieve AudioTrack pointer for setMarkerPosition()"); 760 return (jint)AUDIO_JAVA_ERROR; 761 } 762 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) ); 763} 764 765 766// ---------------------------------------------------------------------------- 767static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) { 768 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 769 uint32_t markerPos = 0; 770 771 if (lpTrack == NULL) { 772 jniThrowException(env, "java/lang/IllegalStateException", 773 "Unable to retrieve AudioTrack pointer for getMarkerPosition()"); 774 return (jint)AUDIO_JAVA_ERROR; 775 } 776 lpTrack->getMarkerPosition(&markerPos); 777 return (jint)markerPos; 778} 779 780 781// ---------------------------------------------------------------------------- 782static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz, 783 jint period) { 784 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 785 if (lpTrack == NULL) { 786 jniThrowException(env, "java/lang/IllegalStateException", 787 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()"); 788 return (jint)AUDIO_JAVA_ERROR; 789 } 790 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) ); 791} 792 793 794// ---------------------------------------------------------------------------- 795static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) { 796 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 797 uint32_t period = 0; 798 799 if (lpTrack == NULL) { 800 jniThrowException(env, "java/lang/IllegalStateException", 801 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()"); 802 return (jint)AUDIO_JAVA_ERROR; 803 } 804 lpTrack->getPositionUpdatePeriod(&period); 805 return (jint)period; 806} 807 808 809// ---------------------------------------------------------------------------- 810static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz, 811 jint position) { 812 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 813 if (lpTrack == NULL) { 814 jniThrowException(env, "java/lang/IllegalStateException", 815 "Unable to retrieve AudioTrack pointer for setPosition()"); 816 return (jint)AUDIO_JAVA_ERROR; 817 } 818 return nativeToJavaStatus( lpTrack->setPosition(position) ); 819} 820 821 822// ---------------------------------------------------------------------------- 823static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) { 824 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 825 uint32_t position = 0; 826 827 if (lpTrack == NULL) { 828 jniThrowException(env, "java/lang/IllegalStateException", 829 "Unable to retrieve AudioTrack pointer for getPosition()"); 830 return (jint)AUDIO_JAVA_ERROR; 831 } 832 lpTrack->getPosition(&position); 833 return (jint)position; 834} 835 836 837// ---------------------------------------------------------------------------- 838static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) { 839 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 840 841 if (lpTrack == NULL) { 842 jniThrowException(env, "java/lang/IllegalStateException", 843 "Unable to retrieve AudioTrack pointer for latency()"); 844 return (jint)AUDIO_JAVA_ERROR; 845 } 846 return (jint)lpTrack->latency(); 847} 848 849 850// ---------------------------------------------------------------------------- 851static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) { 852 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 853 854 if (lpTrack == NULL) { 855 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()"); 856 return (jint)AUDIO_JAVA_ERROR; 857 } 858 AudioTimestamp timestamp; 859 status_t status = lpTrack->getTimestamp(timestamp); 860 if (status == OK) { 861 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL); 862 if (nTimestamp == NULL) { 863 ALOGE("Unable to get array for getTimestamp()"); 864 return (jint)AUDIO_JAVA_ERROR; 865 } 866 nTimestamp[0] = (jlong) timestamp.mPosition; 867 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec); 868 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0); 869 } 870 return (jint) nativeToJavaStatus(status); 871} 872 873 874// ---------------------------------------------------------------------------- 875static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz, 876 jint loopStart, jint loopEnd, jint loopCount) { 877 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 878 if (lpTrack == NULL) { 879 jniThrowException(env, "java/lang/IllegalStateException", 880 "Unable to retrieve AudioTrack pointer for setLoop()"); 881 return (jint)AUDIO_JAVA_ERROR; 882 } 883 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) ); 884} 885 886 887// ---------------------------------------------------------------------------- 888static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { 889 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 890 if (lpTrack == NULL) { 891 jniThrowException(env, "java/lang/IllegalStateException", 892 "Unable to retrieve AudioTrack pointer for reload()"); 893 return (jint)AUDIO_JAVA_ERROR; 894 } 895 return nativeToJavaStatus( lpTrack->reload() ); 896} 897 898 899// ---------------------------------------------------------------------------- 900static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz, 901 jint javaStreamType) { 902 uint32_t afSamplingRate; 903 // convert the stream type from Java to native value 904 // FIXME: code duplication with android_media_AudioTrack_setup() 905 audio_stream_type_t nativeStreamType; 906 switch (javaStreamType) { 907 case AUDIO_STREAM_VOICE_CALL: 908 case AUDIO_STREAM_SYSTEM: 909 case AUDIO_STREAM_RING: 910 case AUDIO_STREAM_MUSIC: 911 case AUDIO_STREAM_ALARM: 912 case AUDIO_STREAM_NOTIFICATION: 913 case AUDIO_STREAM_BLUETOOTH_SCO: 914 case AUDIO_STREAM_DTMF: 915 nativeStreamType = (audio_stream_type_t) javaStreamType; 916 break; 917 default: 918 nativeStreamType = AUDIO_STREAM_DEFAULT; 919 break; 920 } 921 922 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType); 923 if (status != NO_ERROR) { 924 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d " 925 "in AudioTrack JNI", status, nativeStreamType); 926 return DEFAULT_OUTPUT_SAMPLE_RATE; 927 } else { 928 return afSamplingRate; 929 } 930} 931 932 933// ---------------------------------------------------------------------------- 934// returns the minimum required size for the successful creation of a streaming AudioTrack 935// returns -1 if there was an error querying the hardware. 936static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, 937 jint sampleRateInHertz, jint channelCount, jint audioFormat) { 938 939 size_t frameCount; 940 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT, 941 sampleRateInHertz); 942 if (status != NO_ERROR) { 943 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d", 944 sampleRateInHertz, status); 945 return -1; 946 } 947 const audio_format_t format = audioFormatToNative(audioFormat); 948 if (audio_is_linear_pcm(format)) { 949 const size_t bytesPerSample = audio_bytes_per_sample(format); 950 return frameCount * channelCount * bytesPerSample; 951 } else { 952 return frameCount; 953 } 954} 955 956// ---------------------------------------------------------------------------- 957static jint 958android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level ) 959{ 960 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 961 if (lpTrack == NULL ) { 962 jniThrowException(env, "java/lang/IllegalStateException", 963 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()"); 964 return -1; 965 } 966 967 status_t status = lpTrack->setAuxEffectSendLevel(level); 968 if (status != NO_ERROR) { 969 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d", 970 level, status); 971 } 972 return (jint) status; 973} 974 975// ---------------------------------------------------------------------------- 976static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz, 977 jint effectId) { 978 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); 979 if (lpTrack == NULL) { 980 jniThrowException(env, "java/lang/IllegalStateException", 981 "Unable to retrieve AudioTrack pointer for attachAuxEffect()"); 982 return (jint)AUDIO_JAVA_ERROR; 983 } 984 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) ); 985} 986 987// ---------------------------------------------------------------------------- 988// ---------------------------------------------------------------------------- 989static const JNINativeMethod gMethods[] = { 990 // name, signature, funcPtr 991 {"native_start", "()V", (void *)android_media_AudioTrack_start}, 992 {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, 993 {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, 994 {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, 995 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIIII[I)I", 996 (void *)android_media_AudioTrack_setup}, 997 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, 998 {"native_release", "()V", (void *)android_media_AudioTrack_release}, 999 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte}, 1000 {"native_write_native_bytes", 1001 "(Ljava/lang/Object;IIIZ)I", 1002 (void *)android_media_AudioTrack_write_native_bytes}, 1003 {"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short}, 1004 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_write_float}, 1005 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, 1006 {"native_get_native_frame_count", 1007 "()I", (void *)android_media_AudioTrack_get_native_frame_count}, 1008 {"native_set_playback_rate", 1009 "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, 1010 {"native_get_playback_rate", 1011 "()I", (void *)android_media_AudioTrack_get_playback_rate}, 1012 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, 1013 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, 1014 {"native_set_pos_update_period", 1015 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, 1016 {"native_get_pos_update_period", 1017 "()I", (void *)android_media_AudioTrack_get_pos_update_period}, 1018 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, 1019 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, 1020 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, 1021 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, 1022 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, 1023 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, 1024 {"native_get_output_sample_rate", 1025 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate}, 1026 {"native_get_min_buff_size", 1027 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, 1028 {"native_setAuxEffectSendLevel", 1029 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel}, 1030 {"native_attachAuxEffect", 1031 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, 1032}; 1033 1034 1035// field names found in android/media/AudioTrack.java 1036#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 1037#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" 1038#define JAVA_JNIDATA_FIELD_NAME "mJniData" 1039#define JAVA_STREAMTYPE_FIELD_NAME "mStreamType" 1040 1041// ---------------------------------------------------------------------------- 1042// preconditions: 1043// theClass is valid 1044bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className, 1045 const char* constName, int* constVal) { 1046 jfieldID javaConst = NULL; 1047 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I"); 1048 if (javaConst != NULL) { 1049 *constVal = pEnv->GetStaticIntField(theClass, javaConst); 1050 return true; 1051 } else { 1052 ALOGE("Can't find %s.%s", className, constName); 1053 return false; 1054 } 1055} 1056 1057 1058// ---------------------------------------------------------------------------- 1059int register_android_media_AudioTrack(JNIEnv *env) 1060{ 1061 javaAudioTrackFields.nativeTrackInJavaObj = NULL; 1062 javaAudioTrackFields.postNativeEventInJava = NULL; 1063 1064 // Get the AudioTrack class 1065 jclass audioTrackClass = FindClassOrDie(env, kClassPathName); 1066 1067 // Get the postEvent method 1068 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, 1069 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME, 1070 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 1071 1072 // Get the variables fields 1073 // nativeTrackInJavaObj 1074 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env, 1075 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J"); 1076 // jniData 1077 javaAudioTrackFields.jniData = GetFieldIDOrDie(env, 1078 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J"); 1079 // fieldStreamType 1080 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env, 1081 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I"); 1082 1083 // Get the AudioAttributes class and fields 1084 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName); 1085 javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I"); 1086 javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env, 1087 audioAttrClass, "mContentType", "I"); 1088 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I"); 1089 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env, 1090 audioAttrClass, "mFormattedTags", "Ljava/lang/String;"); 1091 1092 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); 1093} 1094 1095 1096// ---------------------------------------------------------------------------- 1097