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