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