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