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