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