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