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