android_media_AudioTrack.cpp revision d24b8183b93e781080b2c16c487e60d51c12da31
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 <stdio.h> 21#include <unistd.h> 22#include <fcntl.h> 23#include <math.h> 24 25#include "jni.h" 26#include "JNIHelp.h" 27#include "android_runtime/AndroidRuntime.h" 28 29#include "utils/Log.h" 30#include "media/AudioSystem.h" 31#include "media/AudioTrack.h" 32 33#include <utils/MemoryHeapBase.h> 34#include <utils/MemoryBase.h> 35 36 37// ---------------------------------------------------------------------------- 38 39using namespace android; 40 41// ---------------------------------------------------------------------------- 42static const char* const kClassPathName = "android/media/AudioTrack"; 43 44struct fields_t { 45 // these fields provide access from C++ to the... 46 jclass audioTrackClass; //... AudioTrack class 47 jmethodID postNativeEventInJava; //... event post callback method 48 int PCM16; //... format constants 49 int PCM8; //... format constants 50 int STREAM_VOICE_CALL; //... stream type constants 51 int STREAM_SYSTEM; //... stream type constants 52 int STREAM_RING; //... stream type constants 53 int STREAM_MUSIC; //... stream type constants 54 int STREAM_ALARM; //... stream type constants 55 int STREAM_NOTIFICATION; //... stream type constants 56 int STREAM_BLUETOOTH_SCO; //... stream type constants 57 int MODE_STREAM; //... memory mode 58 int MODE_STATIC; //... memory mode 59 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object 60 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack 61}; 62static fields_t javaAudioTrackFields; 63 64struct audiotrack_callback_cookie { 65 jclass audioTrack_class; 66 jobject audioTrack_ref; 67 }; 68 69// ---------------------------------------------------------------------------- 70class AudioTrackJniStorage { 71 public: 72 sp<MemoryHeapBase> mMemHeap; 73 sp<MemoryBase> mMemBase; 74 audiotrack_callback_cookie mCallbackData; 75 76 AudioTrackJniStorage() { 77 } 78 79 ~AudioTrackJniStorage() { 80 mMemBase.clear(); 81 mMemHeap.clear(); 82 } 83 84 bool allocSharedMem(int sizeInBytes) { 85 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); 86 if (mMemHeap->getHeapID() < 0) { 87 return false; 88 } 89 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes); 90 return true; 91 } 92}; 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_INVALIDCHANNELCOUNT -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 if (event == AudioTrack::EVENT_MORE_DATA) { 125 // set size to 0 to signal we're not using the callback to write more data 126 AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info; 127 pBuff->size = 0; 128 129 } else if (event == AudioTrack::EVENT_MARKER) { 130 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user; 131 JNIEnv *env = AndroidRuntime::getJNIEnv(); 132 if (user && env) { 133 env->CallStaticVoidMethod( 134 callbackInfo->audioTrack_class, 135 javaAudioTrackFields.postNativeEventInJava, 136 callbackInfo->audioTrack_ref, event, 0,0, NULL); 137 if (env->ExceptionCheck()) { 138 env->ExceptionDescribe(); 139 env->ExceptionClear(); 140 } 141 } 142 143 } else if (event == AudioTrack::EVENT_NEW_POS) { 144 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user; 145 JNIEnv *env = AndroidRuntime::getJNIEnv(); 146 if (user && env) { 147 env->CallStaticVoidMethod( 148 callbackInfo->audioTrack_class, 149 javaAudioTrackFields.postNativeEventInJava, 150 callbackInfo->audioTrack_ref, event, 0,0, NULL); 151 if (env->ExceptionCheck()) { 152 env->ExceptionDescribe(); 153 env->ExceptionClear(); 154 } 155 } 156 } 157} 158 159 160// ---------------------------------------------------------------------------- 161static int 162android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, 163 jint streamType, jint sampleRateInHertz, jint nbChannels, 164 jint audioFormat, jint buffSizeInBytes, jint memoryMode) 165{ 166 //LOGV("sampleRate=%d, audioFormat(from Java)=%d, nbChannels=%d, buffSize=%d", 167 // sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes); 168 int afSampleRate; 169 int afFrameCount; 170 171 if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { 172 LOGE("Error creating AudioTrack: Could not get AudioSystem frame count."); 173 return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; 174 } 175 if (AudioSystem::getOutputSamplingRate(&afSampleRate) != NO_ERROR) { 176 LOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate."); 177 return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; 178 } 179 180 if ((nbChannels == 0) || (nbChannels > 2)) { 181 LOGE("Error creating AudioTrack: channel count is not 1 or 2."); 182 return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT; 183 } 184 185 // check the stream type 186 AudioTrack::stream_type atStreamType; 187 if (streamType == javaAudioTrackFields.STREAM_VOICE_CALL) { 188 atStreamType = AudioTrack::VOICE_CALL; 189 } else if (streamType == javaAudioTrackFields.STREAM_SYSTEM) { 190 atStreamType = AudioTrack::SYSTEM; 191 } else if (streamType == javaAudioTrackFields.STREAM_RING) { 192 atStreamType = AudioTrack::RING; 193 } else if (streamType == javaAudioTrackFields.STREAM_MUSIC) { 194 atStreamType = AudioTrack::MUSIC; 195 } else if (streamType == javaAudioTrackFields.STREAM_ALARM) { 196 atStreamType = AudioTrack::ALARM; 197 } else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) { 198 atStreamType = AudioTrack::NOTIFICATION; 199 } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) { 200 atStreamType = AudioTrack::BLUETOOTH_SCO; 201 } else { 202 LOGE("Error creating AudioTrack: unknown stream type."); 203 return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; 204 } 205 206 // check the format. 207 // This function was called from Java, so we compare the format against the Java constants 208 if ((audioFormat != javaAudioTrackFields.PCM16) && (audioFormat != javaAudioTrackFields.PCM8)) { 209 LOGE("Error creating AudioTrack: unsupported audio format."); 210 return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; 211 } 212 213 // compute the frame count 214 int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1; 215 int format = audioFormat == javaAudioTrackFields.PCM16 ? 216 AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT; 217 int frameCount; 218 if (buffSizeInBytes == -1) { 219 // compute the frame count based on the system's output frame count 220 // and the native sample rate 221 frameCount = (sampleRateInHertz*afFrameCount)/afSampleRate; 222 } else { 223 // compute the frame count based on the specified buffer size 224 frameCount = buffSizeInBytes / (nbChannels * bytesPerSample); 225 } 226 227 AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); 228 229 // initialize the callback information: 230 // this data will be passed with every AudioTrack callback 231 jclass clazz = env->GetObjectClass(thiz); 232 if (clazz == NULL) { 233 LOGE("Can't find %s when setting up callback.", kClassPathName); 234 delete lpJniStorage; 235 return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 236 } 237 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); 238 // we use a weak reference so the AudioTrack object can be garbage collected. 239 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); 240 241 // create the native AudioTrack object 242 AudioTrack* lpTrack = new AudioTrack(); 243 if (lpTrack == NULL) { 244 LOGE("Error creating uninitialized AudioTrack"); 245 goto native_track_failure; 246 } 247 248 // initialize the native AudioTrack object 249 if (memoryMode == javaAudioTrackFields.MODE_STREAM) { 250 251 lpTrack->set( 252 atStreamType,// stream type 253 sampleRateInHertz, 254 format,// word length, PCM 255 nbChannels, 256 frameCount, 257 0,// flags 258 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 259 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 260 0,// shared mem 261 true);// thread can call Java 262 263 } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) { 264 // AudioTrack is using shared memory 265 266 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { 267 LOGE("Error creating AudioTrack in static mode: error creating mem heap base"); 268 goto native_init_failure; 269 } 270 271 lpTrack->set( 272 atStreamType,// stream type 273 sampleRateInHertz, 274 format,// word length, PCM 275 nbChannels, 276 frameCount, 277 0,// flags 278 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); 279 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 280 lpJniStorage->mMemBase,// shared mem 281 true);// thread can call Java 282 } 283 284 if (lpTrack->initCheck() != NO_ERROR) { 285 LOGE("Error initializing AudioTrack"); 286 goto native_init_failure; 287 } 288 289 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field 290 // of the Java object (in mNativeTrackInJavaObj) 291 env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack); 292 293 // save the JNI resources so we can free them later 294 //LOGV("storing lpJniStorage: %x\n", (int)lpJniStorage); 295 env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage); 296 297 return AUDIOTRACK_SUCCESS; 298 299 // failures: 300native_init_failure: 301 delete lpTrack; 302 env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0); 303 304native_track_failure: 305 delete lpJniStorage; 306 env->SetIntField(thiz, javaAudioTrackFields.jniData, 0); 307 return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; 308 309} 310 311 312// ---------------------------------------------------------------------------- 313static void 314android_media_AudioTrack_start(JNIEnv *env, jobject thiz) 315{ 316 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 317 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 318 if (lpTrack == NULL ) { 319 jniThrowException(env, "java/lang/IllegalStateException", 320 "Unable to retrieve AudioTrack pointer for start()"); 321 } 322 323 lpTrack->start(); 324} 325 326 327// ---------------------------------------------------------------------------- 328static void 329android_media_AudioTrack_stop(JNIEnv *env, jobject thiz) 330{ 331 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 332 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 333 if (lpTrack == NULL ) { 334 jniThrowException(env, "java/lang/IllegalStateException", 335 "Unable to retrieve AudioTrack pointer for stop()"); 336 } 337 338 lpTrack->stop(); 339} 340 341 342// ---------------------------------------------------------------------------- 343static void 344android_media_AudioTrack_pause(JNIEnv *env, jobject thiz) 345{ 346 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 347 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 348 if (lpTrack == NULL ) { 349 jniThrowException(env, "java/lang/IllegalStateException", 350 "Unable to retrieve AudioTrack pointer for pause()"); 351 } 352 353 lpTrack->pause(); 354} 355 356 357// ---------------------------------------------------------------------------- 358static void 359android_media_AudioTrack_flush(JNIEnv *env, jobject thiz) 360{ 361 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 362 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 363 if (lpTrack == NULL ) { 364 jniThrowException(env, "java/lang/IllegalStateException", 365 "Unable to retrieve AudioTrack pointer for flush()"); 366 } 367 368 lpTrack->flush(); 369} 370 371// ---------------------------------------------------------------------------- 372static void 373android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol ) 374{ 375 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 376 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 377 if (lpTrack == NULL ) { 378 jniThrowException(env, "java/lang/IllegalStateException", 379 "Unable to retrieve AudioTrack pointer for setVolume()"); 380 } 381 382 lpTrack->setVolume(leftVol, rightVol); 383} 384 385// ---------------------------------------------------------------------------- 386static void android_media_AudioTrack_native_finalize(JNIEnv *env, jobject thiz) { 387 LOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz); 388 389 // delete the AudioTrack object 390 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 391 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 392 if (lpTrack) { 393 LOGV("deleting lpTrack: %x\n", (int)lpTrack); 394 lpTrack->stop(); 395 delete lpTrack; 396 } 397 398 // delete the JNI data 399 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField( 400 thiz, javaAudioTrackFields.jniData); 401 if (pJniStorage) { 402 LOGV("deleting pJniStorage: %x\n", (int)pJniStorage); 403 delete pJniStorage; 404 } 405} 406 407// ---------------------------------------------------------------------------- 408static void android_media_AudioTrack_native_release(JNIEnv *env, jobject thiz) { 409 410 // do everything a call to finalize would 411 android_media_AudioTrack_native_finalize(env, thiz); 412 // + reset the native resources in the Java object so any attempt to access 413 // them after a call to release fails. 414 env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0); 415 env->SetIntField(thiz, javaAudioTrackFields.jniData, 0); 416} 417 418 419// ---------------------------------------------------------------------------- 420static jint android_media_AudioTrack_native_write(JNIEnv *env, jobject thiz, 421 jbyteArray javaAudioData, 422 jint offsetInBytes, jint sizeInBytes) { 423 jbyte* cAudioData = NULL; 424 AudioTrack *lpTrack = NULL; 425 //LOGV("android_media_AudioTrack_native_write(offset=%d, sizeInBytes=%d) called", 426 // offsetInBytes, sizeInBytes); 427 428 // get the audio track to load with samples 429 lpTrack = (AudioTrack *)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj); 430 if (lpTrack == NULL) { 431 jniThrowException(env, "java/lang/IllegalStateException", 432 "Unable to retrieve AudioTrack pointer for write()"); 433 } 434 435 // get the pointer for the audio data from the java array 436 if (javaAudioData) { 437 cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL); 438 if (cAudioData == NULL) { 439 LOGE("Error retrieving source of audio data to play, can't play"); 440 return 0; // out of memory or no data to load 441 } 442 } else { 443 LOGE("NULL java array of audio data to play, can't play"); 444 return 0; 445 } 446 447 // give the data to the native AudioTrack object (the data starts at the offset) 448 ssize_t written = 0; 449 // regular write() or copy the data to the AudioTrack's shared memory? 450 if (lpTrack->sharedBuffer() == 0) { 451 written = lpTrack->write(cAudioData + offsetInBytes, sizeInBytes); 452 } else { 453 memcpy(lpTrack->sharedBuffer()->pointer(), cAudioData + offsetInBytes, sizeInBytes); 454 written = sizeInBytes; 455 } 456 457 env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0); 458 459 //LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", 460 // (int)written, (int)(sizeInBytes), (int)offsetInBytes); 461 return (int)written; 462} 463 464 465// ---------------------------------------------------------------------------- 466static jint android_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz, 467 jshortArray javaAudioData, 468 jint offsetInShorts, jint sizeInShorts) { 469 return (android_media_AudioTrack_native_write(env, thiz, 470 (jbyteArray) javaAudioData, 471 offsetInShorts*2, sizeInShorts*2) 472 / 2); 473} 474 475 476// ---------------------------------------------------------------------------- 477static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) { 478 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 479 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 480 481 if (lpTrack) { 482 return lpTrack->frameCount(); 483 } else { 484 jniThrowException(env, "java/lang/IllegalStateException", 485 "Unable to retrieve AudioTrack pointer for frameCount()"); 486 return AUDIOTRACK_ERROR; 487 } 488} 489 490 491// ---------------------------------------------------------------------------- 492static void android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, 493 jint sampleRateInHz) { 494 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 495 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 496 497 if (lpTrack) { 498 lpTrack->setSampleRate(sampleRateInHz); 499 } else { 500 jniThrowException(env, "java/lang/IllegalStateException", 501 "Unable to retrieve AudioTrack pointer for setSampleRate()"); 502 } 503} 504 505 506// ---------------------------------------------------------------------------- 507static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) { 508 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 509 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 510 511 if (lpTrack) { 512 return (jint) lpTrack->getSampleRate(); 513 } else { 514 jniThrowException(env, "java/lang/IllegalStateException", 515 "Unable to retrieve AudioTrack pointer for getSampleRate()"); 516 return AUDIOTRACK_ERROR; 517 } 518} 519 520 521// ---------------------------------------------------------------------------- 522static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz, 523 jint markerPos) { 524 525 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 526 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 527 528 if (lpTrack) { 529 return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) ); 530 } else { 531 jniThrowException(env, "java/lang/IllegalStateException", 532 "Unable to retrieve AudioTrack pointer for setMarkerPosition()"); 533 return AUDIOTRACK_ERROR; 534 } 535} 536 537 538// ---------------------------------------------------------------------------- 539static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) { 540 541 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 542 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 543 uint32_t markerPos = 0; 544 545 if (lpTrack) { 546 lpTrack->getMarkerPosition(&markerPos); 547 return (jint)markerPos; 548 } else { 549 jniThrowException(env, "java/lang/IllegalStateException", 550 "Unable to retrieve AudioTrack pointer for getMarkerPosition()"); 551 return AUDIOTRACK_ERROR; 552 } 553} 554 555 556// ---------------------------------------------------------------------------- 557static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz, 558 jint period) { 559 560 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 561 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 562 563 if (lpTrack) { 564 return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) ); 565 } else { 566 jniThrowException(env, "java/lang/IllegalStateException", 567 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()"); 568 return AUDIOTRACK_ERROR; 569 } 570} 571 572 573// ---------------------------------------------------------------------------- 574static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) { 575 576 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 577 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 578 uint32_t period = 0; 579 580 if (lpTrack) { 581 lpTrack->getPositionUpdatePeriod(&period); 582 return (jint)period; 583 } else { 584 jniThrowException(env, "java/lang/IllegalStateException", 585 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()"); 586 return AUDIOTRACK_ERROR; 587 } 588} 589 590 591// ---------------------------------------------------------------------------- 592static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz, 593 jint position) { 594 595 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 596 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 597 598 if (lpTrack) { 599 return android_media_translateErrorCode( lpTrack->setPosition(position) ); 600 } else { 601 jniThrowException(env, "java/lang/IllegalStateException", 602 "Unable to retrieve AudioTrack pointer for setPosition()"); 603 return AUDIOTRACK_ERROR; 604 } 605} 606 607 608// ---------------------------------------------------------------------------- 609static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) { 610 611 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 612 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 613 uint32_t position = 0; 614 615 if (lpTrack) { 616 lpTrack->getPosition(&position); 617 return (jint)position; 618 } else { 619 jniThrowException(env, "java/lang/IllegalStateException", 620 "Unable to retrieve AudioTrack pointer for getPosition()"); 621 return AUDIOTRACK_ERROR; 622 } 623} 624 625 626// ---------------------------------------------------------------------------- 627static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz, 628 jint loopStart, jint loopEnd, jint loopCount) { 629 630 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 631 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 632 if (lpTrack) { 633 return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) ); 634 } else { 635 jniThrowException(env, "java/lang/IllegalStateException", 636 "Unable to retrieve AudioTrack pointer for setLoop()"); 637 return AUDIOTRACK_ERROR; 638 } 639} 640 641 642// ---------------------------------------------------------------------------- 643static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) { 644 645 AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( 646 thiz, javaAudioTrackFields.nativeTrackInJavaObj); 647 if (lpTrack) { 648 return android_media_translateErrorCode( lpTrack->reload() ); 649 } else { 650 jniThrowException(env, "java/lang/IllegalStateException", 651 "Unable to retrieve AudioTrack pointer for reload()"); 652 return AUDIOTRACK_ERROR; 653 } 654} 655 656 657// ---------------------------------------------------------------------------- 658static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz) { 659 int afSamplingRate; 660 if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { 661 return DEFAULT_OUTPUT_SAMPLE_RATE; 662 } else { 663 return afSamplingRate; 664 } 665} 666 667 668// ---------------------------------------------------------------------------- 669// returns the minimum required size for the successful creation of a streaming AudioTrack 670static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, 671 jint sampleRateInHertz, jint nbChannels, jint audioFormat) { 672 int afSamplingRate; 673 int afFrameCount; 674 uint32_t afLatency; 675 676 if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) { 677 return -1; 678 } 679 if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) { 680 return -1; 681 } 682 683 if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) { 684 return -1; 685 } 686 687 // Ensure that buffer depth covers at least audio hardware latency 688 uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate); 689 uint32_t minFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate; 690 int minBuffSize = minFrameCount 691 * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1) 692 * nbChannels; 693 return minBuffSize; 694} 695 696 697// ---------------------------------------------------------------------------- 698// ---------------------------------------------------------------------------- 699static JNINativeMethod gMethods[] = { 700 // name, signature, funcPtr 701 {"native_start", "()V", (void *)android_media_AudioTrack_start}, 702 {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, 703 {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, 704 {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, 705 {"native_setup", "(Ljava/lang/Object;IIIIII)I", 706 (void *)android_media_AudioTrack_native_setup}, 707 {"native_finalize", "()V", (void *)android_media_AudioTrack_native_finalize}, 708 {"native_release", "()V", (void *)android_media_AudioTrack_native_release}, 709 {"native_write_byte", "([BII)I", (void *)android_media_AudioTrack_native_write}, 710 {"native_write_short", "([SII)I", (void *)android_media_AudioTrack_native_write_short}, 711 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, 712 {"native_get_native_frame_count", 713 "()I", (void *)android_media_AudioTrack_get_native_frame_count}, 714 {"native_set_playback_rate", 715 "(I)V", (void *)android_media_AudioTrack_set_playback_rate}, 716 {"native_get_playback_rate", 717 "()I", (void *)android_media_AudioTrack_get_playback_rate}, 718 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, 719 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, 720 {"native_set_pos_update_period", 721 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, 722 {"native_get_pos_update_period", 723 "()I", (void *)android_media_AudioTrack_get_pos_update_period}, 724 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, 725 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, 726 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, 727 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, 728 {"native_get_output_sample_rate", 729 "()I", (void *)android_media_AudioTrack_get_output_sample_rate}, 730 {"native_get_min_buff_size", 731 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, 732}; 733 734 735// field names found in android/media/AudioTrack.java 736#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 737#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" 738#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" 739#define JAVA_CONST_BUFFER_COUNT_NAME "BUFFER_COUNT" 740#define JAVA_CONST_STREAM_VOICE_CALL_NAME "STREAM_VOICE_CALL" 741#define JAVA_CONST_STREAM_SYSTEM_NAME "STREAM_SYSTEM" 742#define JAVA_CONST_STREAM_RING_NAME "STREAM_RING" 743#define JAVA_CONST_STREAM_MUSIC_NAME "STREAM_MUSIC" 744#define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM" 745#define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION" 746#define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME "STREAM_BLUETOOTH_SCO" 747#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM" 748#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC" 749#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" 750#define JAVA_JNIDATA_FIELD_NAME "mJniData" 751 752#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" 753#define JAVA_AUDIOMANAGER_CLASS_NAME "android/media/AudioManager" 754 755// ---------------------------------------------------------------------------- 756// preconditions: 757// theClass is valid 758bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className, 759 const char* constName, int* constVal) { 760 jfieldID javaConst = NULL; 761 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I"); 762 if (javaConst != NULL) { 763 *constVal = pEnv->GetStaticIntField(theClass, javaConst); 764 return true; 765 } else { 766 LOGE("Can't find %s.%s", className, constName); 767 return false; 768 } 769} 770 771 772// ---------------------------------------------------------------------------- 773int register_android_media_AudioTrack(JNIEnv *env) 774{ 775 javaAudioTrackFields.audioTrackClass = NULL; 776 javaAudioTrackFields.nativeTrackInJavaObj = NULL; 777 javaAudioTrackFields.postNativeEventInJava = NULL; 778 779 // Get the AudioTrack class 780 javaAudioTrackFields.audioTrackClass = env->FindClass(kClassPathName); 781 if (javaAudioTrackFields.audioTrackClass == NULL) { 782 LOGE("Can't find %s", kClassPathName); 783 return -1; 784 } 785 786 // Get the postEvent method 787 javaAudioTrackFields.postNativeEventInJava = env->GetStaticMethodID( 788 javaAudioTrackFields.audioTrackClass, 789 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 790 if (javaAudioTrackFields.postNativeEventInJava == NULL) { 791 LOGE("Can't find AudioTrack.%s", JAVA_POSTEVENT_CALLBACK_NAME); 792 return -1; 793 } 794 795 // Get the variables fields 796 // nativeTrackInJavaObj 797 javaAudioTrackFields.nativeTrackInJavaObj = env->GetFieldID( 798 javaAudioTrackFields.audioTrackClass, 799 JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "I"); 800 if (javaAudioTrackFields.nativeTrackInJavaObj == NULL) { 801 LOGE("Can't find AudioTrack.%s", JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME); 802 return -1; 803 } 804 // jniData; 805 javaAudioTrackFields.jniData = env->GetFieldID( 806 javaAudioTrackFields.audioTrackClass, 807 JAVA_JNIDATA_FIELD_NAME, "I"); 808 if (javaAudioTrackFields.jniData == NULL) { 809 LOGE("Can't find AudioTrack.%s", JAVA_JNIDATA_FIELD_NAME); 810 return -1; 811 } 812 813 // Get the memory mode constants 814 if ( !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass, 815 kClassPathName, 816 JAVA_CONST_MODE_STATIC_NAME, &(javaAudioTrackFields.MODE_STATIC)) 817 || !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass, 818 kClassPathName, 819 JAVA_CONST_MODE_STREAM_NAME, &(javaAudioTrackFields.MODE_STREAM)) ) { 820 // error log performed in android_media_getIntConstantFromClass() 821 return -1; 822 } 823 824 // Get the format constants from the AudioFormat class 825 jclass audioFormatClass = NULL; 826 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); 827 if (audioFormatClass == NULL) { 828 LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME); 829 return -1; 830 } 831 if ( !android_media_getIntConstantFromClass(env, audioFormatClass, 832 JAVA_AUDIOFORMAT_CLASS_NAME, 833 JAVA_CONST_PCM16_NAME, &(javaAudioTrackFields.PCM16)) 834 || !android_media_getIntConstantFromClass(env, audioFormatClass, 835 JAVA_AUDIOFORMAT_CLASS_NAME, 836 JAVA_CONST_PCM8_NAME, &(javaAudioTrackFields.PCM8)) ) { 837 // error log performed in android_media_getIntConstantFromClass() 838 return -1; 839 } 840 841 // Get the stream types from the AudioManager class 842 jclass audioManagerClass = NULL; 843 audioManagerClass = env->FindClass(JAVA_AUDIOMANAGER_CLASS_NAME); 844 if (audioManagerClass == NULL) { 845 LOGE("Can't find %s", JAVA_AUDIOMANAGER_CLASS_NAME); 846 return -1; 847 } 848 if ( !android_media_getIntConstantFromClass(env, audioManagerClass, 849 JAVA_AUDIOMANAGER_CLASS_NAME, 850 JAVA_CONST_STREAM_VOICE_CALL_NAME, &(javaAudioTrackFields.STREAM_VOICE_CALL)) 851 || !android_media_getIntConstantFromClass(env, audioManagerClass, 852 JAVA_AUDIOMANAGER_CLASS_NAME, 853 JAVA_CONST_STREAM_MUSIC_NAME, &(javaAudioTrackFields.STREAM_MUSIC)) 854 || !android_media_getIntConstantFromClass(env, audioManagerClass, 855 JAVA_AUDIOMANAGER_CLASS_NAME, 856 JAVA_CONST_STREAM_SYSTEM_NAME, &(javaAudioTrackFields.STREAM_SYSTEM)) 857 || !android_media_getIntConstantFromClass(env, audioManagerClass, 858 JAVA_AUDIOMANAGER_CLASS_NAME, 859 JAVA_CONST_STREAM_RING_NAME, &(javaAudioTrackFields.STREAM_RING)) 860 || !android_media_getIntConstantFromClass(env, audioManagerClass, 861 JAVA_AUDIOMANAGER_CLASS_NAME, 862 JAVA_CONST_STREAM_ALARM_NAME, &(javaAudioTrackFields.STREAM_ALARM)) 863 || !android_media_getIntConstantFromClass(env, audioManagerClass, 864 JAVA_AUDIOMANAGER_CLASS_NAME, 865 JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION)) 866 || !android_media_getIntConstantFromClass(env, audioManagerClass, 867 JAVA_AUDIOMANAGER_CLASS_NAME, 868 JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, 869 &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) { 870 // error log performed in android_media_getIntConstantFromClass() 871 return -1; 872 } 873 874 return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); 875} 876 877 878// ---------------------------------------------------------------------------- 879