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 "android_media_AudioTrack.h"
21
22 #include <JNIHelp.h>
23 #include <JniConstants.h>
24 #include "core_jni_helpers.h"
25
26 #include "ScopedBytes.h"
27
28 #include <utils/Log.h>
29 #include <media/AudioSystem.h>
30 #include <media/AudioTrack.h>
31 #include <audio_utils/primitives.h>
32
33 #include <binder/MemoryHeapBase.h>
34 #include <binder/MemoryBase.h>
35
36 #include "android_media_AudioFormat.h"
37 #include "android_media_AudioErrors.h"
38 #include "android_media_PlaybackParams.h"
39 #include "android_media_DeviceCallback.h"
40 #include "android_media_VolumeShaper.h"
41
42 #include <cinttypes>
43
44 // ----------------------------------------------------------------------------
45
46 using namespace android;
47
48 // ----------------------------------------------------------------------------
49 static const char* const kClassPathName = "android/media/AudioTrack";
50 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
51
52 struct audio_track_fields_t {
53 // these fields provide access from C++ to the...
54 jmethodID postNativeEventInJava; //... event post callback method
55 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object
56 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack
57 jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object
58 };
59 struct audio_attributes_fields_t {
60 jfieldID fieldUsage; // AudioAttributes.mUsage
61 jfieldID fieldContentType; // AudioAttributes.mContentType
62 jfieldID fieldFlags; // AudioAttributes.mFlags
63 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
64 };
65 static audio_track_fields_t javaAudioTrackFields;
66 static audio_attributes_fields_t javaAudioAttrFields;
67 static PlaybackParams::fields_t gPlaybackParamsFields;
68 static VolumeShaperHelper::fields_t gVolumeShaperFields;
69
70 struct audiotrack_callback_cookie {
71 jclass audioTrack_class;
72 jobject audioTrack_ref;
73 bool busy;
74 Condition cond;
75 };
76
77 // keep these values in sync with AudioTrack.java
78 #define MODE_STATIC 0
79 #define MODE_STREAM 1
80
81 // ----------------------------------------------------------------------------
82 class AudioTrackJniStorage {
83 public:
84 sp<MemoryHeapBase> mMemHeap;
85 sp<MemoryBase> mMemBase;
86 audiotrack_callback_cookie mCallbackData;
87 sp<JNIDeviceCallback> mDeviceCallback;
88
AudioTrackJniStorage()89 AudioTrackJniStorage() {
90 mCallbackData.audioTrack_class = 0;
91 mCallbackData.audioTrack_ref = 0;
92 }
93
~AudioTrackJniStorage()94 ~AudioTrackJniStorage() {
95 mMemBase.clear();
96 mMemHeap.clear();
97 }
98
allocSharedMem(int sizeInBytes)99 bool allocSharedMem(int sizeInBytes) {
100 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
101 if (mMemHeap->getHeapID() < 0) {
102 return false;
103 }
104 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
105 return true;
106 }
107 };
108
109 static Mutex sLock;
110 static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
111
112 // ----------------------------------------------------------------------------
113 #define DEFAULT_OUTPUT_SAMPLE_RATE 44100
114
115 #define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM (-16)
116 #define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK (-17)
117 #define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT (-18)
118 #define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19)
119 #define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20)
120
121 // ----------------------------------------------------------------------------
audioCallback(int event,void * user,void * info)122 static void audioCallback(int event, void* user, void *info) {
123
124 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
125 {
126 Mutex::Autolock l(sLock);
127 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
128 return;
129 }
130 callbackInfo->busy = true;
131 }
132
133 switch (event) {
134 case AudioTrack::EVENT_MARKER: {
135 JNIEnv *env = AndroidRuntime::getJNIEnv();
136 if (user != NULL && env != NULL) {
137 env->CallStaticVoidMethod(
138 callbackInfo->audioTrack_class,
139 javaAudioTrackFields.postNativeEventInJava,
140 callbackInfo->audioTrack_ref, event, 0,0, NULL);
141 if (env->ExceptionCheck()) {
142 env->ExceptionDescribe();
143 env->ExceptionClear();
144 }
145 }
146 } break;
147
148 case AudioTrack::EVENT_NEW_POS: {
149 JNIEnv *env = AndroidRuntime::getJNIEnv();
150 if (user != NULL && env != NULL) {
151 env->CallStaticVoidMethod(
152 callbackInfo->audioTrack_class,
153 javaAudioTrackFields.postNativeEventInJava,
154 callbackInfo->audioTrack_ref, event, 0,0, NULL);
155 if (env->ExceptionCheck()) {
156 env->ExceptionDescribe();
157 env->ExceptionClear();
158 }
159 }
160 } break;
161 }
162
163 {
164 Mutex::Autolock l(sLock);
165 callbackInfo->busy = false;
166 callbackInfo->cond.broadcast();
167 }
168 }
169
170
171 // ----------------------------------------------------------------------------
getAudioTrack(JNIEnv * env,jobject thiz)172 static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
173 {
174 Mutex::Autolock l(sLock);
175 AudioTrack* const at =
176 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
177 return sp<AudioTrack>(at);
178 }
179
setAudioTrack(JNIEnv * env,jobject thiz,const sp<AudioTrack> & at)180 static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
181 {
182 Mutex::Autolock l(sLock);
183 sp<AudioTrack> old =
184 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
185 if (at.get()) {
186 at->incStrong((void*)setAudioTrack);
187 }
188 if (old != 0) {
189 old->decStrong((void*)setAudioTrack);
190 }
191 env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get());
192 return old;
193 }
194
195 // ----------------------------------------------------------------------------
android_media_AudioTrack_getAudioTrack(JNIEnv * env,jobject audioTrackObj)196 sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) {
197 return getAudioTrack(env, audioTrackObj);
198 }
199
200 // This function converts Java channel masks to a native channel mask.
201 // validity should be checked with audio_is_output_channel().
nativeChannelMaskFromJavaChannelMasks(jint channelPositionMask,jint channelIndexMask)202 static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
203 jint channelPositionMask, jint channelIndexMask)
204 {
205 if (channelIndexMask != 0) { // channel index mask takes priority
206 // To convert to a native channel mask, the Java channel index mask
207 // requires adding the index representation.
208 return audio_channel_mask_from_representation_and_bits(
209 AUDIO_CHANNEL_REPRESENTATION_INDEX,
210 channelIndexMask);
211 }
212 // To convert to a native channel mask, the Java channel position mask
213 // requires a shift by 2 to skip the two deprecated channel
214 // configurations "default" and "mono".
215 return (audio_channel_mask_t)(channelPositionMask >> 2);
216 }
217
218 // ----------------------------------------------------------------------------
219 static jint
android_media_AudioTrack_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jintArray jSampleRate,jint channelPositionMask,jint channelIndexMask,jint audioFormat,jint buffSizeInBytes,jint memoryMode,jintArray jSession,jlong nativeAudioTrack)220 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
221 jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
222 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
223 jlong nativeAudioTrack) {
224
225 ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
226 "nativeAudioTrack=0x%" PRIX64,
227 jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
228 nativeAudioTrack);
229
230 sp<AudioTrack> lpTrack = 0;
231
232 if (jSession == NULL) {
233 ALOGE("Error creating AudioTrack: invalid session ID pointer");
234 return (jint) AUDIO_JAVA_ERROR;
235 }
236
237 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
238 if (nSession == NULL) {
239 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
240 return (jint) AUDIO_JAVA_ERROR;
241 }
242 audio_session_t sessionId = (audio_session_t) nSession[0];
243 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
244 nSession = NULL;
245
246 AudioTrackJniStorage* lpJniStorage = NULL;
247
248 audio_attributes_t *paa = NULL;
249
250 jclass clazz = env->GetObjectClass(thiz);
251 if (clazz == NULL) {
252 ALOGE("Can't find %s when setting up callback.", kClassPathName);
253 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
254 }
255
256 // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
257 if (nativeAudioTrack == 0) {
258 if (jaa == 0) {
259 ALOGE("Error creating AudioTrack: invalid audio attributes");
260 return (jint) AUDIO_JAVA_ERROR;
261 }
262
263 if (jSampleRate == 0) {
264 ALOGE("Error creating AudioTrack: invalid sample rates");
265 return (jint) AUDIO_JAVA_ERROR;
266 }
267
268 int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
269 int sampleRateInHertz = sampleRates[0];
270 env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
271
272 // Invalid channel representations are caught by !audio_is_output_channel() below.
273 audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
274 channelPositionMask, channelIndexMask);
275 if (!audio_is_output_channel(nativeChannelMask)) {
276 ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
277 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
278 }
279
280 uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
281
282 // check the format.
283 // This function was called from Java, so we compare the format against the Java constants
284 audio_format_t format = audioFormatToNative(audioFormat);
285 if (format == AUDIO_FORMAT_INVALID) {
286 ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
287 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
288 }
289
290 // compute the frame count
291 size_t frameCount;
292 if (audio_is_linear_pcm(format)) {
293 const size_t bytesPerSample = audio_bytes_per_sample(format);
294 frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
295 } else {
296 frameCount = buffSizeInBytes;
297 }
298
299 // create the native AudioTrack object
300 lpTrack = new AudioTrack();
301
302 // read the AudioAttributes values
303 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
304 const jstring jtags =
305 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
306 const char* tags = env->GetStringUTFChars(jtags, NULL);
307 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
308 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
309 env->ReleaseStringUTFChars(jtags, tags);
310 paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
311 paa->content_type =
312 (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
313 paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
314
315 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
316 paa->usage, paa->content_type, paa->flags, paa->tags);
317
318 // initialize the callback information:
319 // this data will be passed with every AudioTrack callback
320 lpJniStorage = new AudioTrackJniStorage();
321 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
322 // we use a weak reference so the AudioTrack object can be garbage collected.
323 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
324 lpJniStorage->mCallbackData.busy = false;
325
326 // initialize the native AudioTrack object
327 status_t status = NO_ERROR;
328 switch (memoryMode) {
329 case MODE_STREAM:
330
331 status = lpTrack->set(
332 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
333 sampleRateInHertz,
334 format,// word length, PCM
335 nativeChannelMask,
336 frameCount,
337 AUDIO_OUTPUT_FLAG_NONE,
338 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
339 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
340 0,// shared mem
341 true,// thread can call Java
342 sessionId,// audio session ID
343 AudioTrack::TRANSFER_SYNC,
344 NULL, // default offloadInfo
345 -1, -1, // default uid, pid values
346 paa);
347 break;
348
349 case MODE_STATIC:
350 // AudioTrack is using shared memory
351
352 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
353 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
354 goto native_init_failure;
355 }
356
357 status = lpTrack->set(
358 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
359 sampleRateInHertz,
360 format,// word length, PCM
361 nativeChannelMask,
362 frameCount,
363 AUDIO_OUTPUT_FLAG_NONE,
364 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
365 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
366 lpJniStorage->mMemBase,// shared mem
367 true,// thread can call Java
368 sessionId,// audio session ID
369 AudioTrack::TRANSFER_SHARED,
370 NULL, // default offloadInfo
371 -1, -1, // default uid, pid values
372 paa);
373 break;
374
375 default:
376 ALOGE("Unknown mode %d", memoryMode);
377 goto native_init_failure;
378 }
379
380 if (status != NO_ERROR) {
381 ALOGE("Error %d initializing AudioTrack", status);
382 goto native_init_failure;
383 }
384 } else { // end if (nativeAudioTrack == 0)
385 lpTrack = (AudioTrack*)nativeAudioTrack;
386 // TODO: We need to find out which members of the Java AudioTrack might
387 // need to be initialized from the Native AudioTrack
388 // these are directly returned from getters:
389 // mSampleRate
390 // mAudioFormat
391 // mStreamType
392 // mChannelConfiguration
393 // mChannelCount
394 // mState (?)
395 // mPlayState (?)
396 // these may be used internally (Java AudioTrack.audioParamCheck():
397 // mChannelMask
398 // mChannelIndexMask
399 // mDataLoadMode
400
401 // initialize the callback information:
402 // this data will be passed with every AudioTrack callback
403 lpJniStorage = new AudioTrackJniStorage();
404 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
405 // we use a weak reference so the AudioTrack object can be garbage collected.
406 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
407 lpJniStorage->mCallbackData.busy = false;
408 }
409
410 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
411 if (nSession == NULL) {
412 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
413 goto native_init_failure;
414 }
415 // read the audio session ID back from AudioTrack in case we create a new session
416 nSession[0] = lpTrack->getSessionId();
417 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
418 nSession = NULL;
419
420 {
421 const jint elements[1] = { (jint) lpTrack->getSampleRate() };
422 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
423 }
424
425 { // scope for the lock
426 Mutex::Autolock l(sLock);
427 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
428 }
429 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
430 // of the Java object (in mNativeTrackInJavaObj)
431 setAudioTrack(env, thiz, lpTrack);
432
433 // save the JNI resources so we can free them later
434 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
435 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
436
437 // since we had audio attributes, the stream type was derived from them during the
438 // creation of the native AudioTrack: push the same value to the Java object
439 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
440 if (paa != NULL) {
441 // audio attributes were copied in AudioTrack creation
442 free(paa);
443 paa = NULL;
444 }
445
446
447 return (jint) AUDIO_JAVA_SUCCESS;
448
449 // failures:
450 native_init_failure:
451 if (paa != NULL) {
452 free(paa);
453 }
454 if (nSession != NULL) {
455 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
456 }
457 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
458 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
459 delete lpJniStorage;
460 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
461
462 // lpTrack goes out of scope, so reference count drops to zero
463 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
464 }
465
466 // ----------------------------------------------------------------------------
467 static void
android_media_AudioTrack_start(JNIEnv * env,jobject thiz)468 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
469 {
470 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
471 if (lpTrack == NULL) {
472 jniThrowException(env, "java/lang/IllegalStateException",
473 "Unable to retrieve AudioTrack pointer for start()");
474 return;
475 }
476
477 lpTrack->start();
478 }
479
480
481 // ----------------------------------------------------------------------------
482 static void
android_media_AudioTrack_stop(JNIEnv * env,jobject thiz)483 android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
484 {
485 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
486 if (lpTrack == NULL) {
487 jniThrowException(env, "java/lang/IllegalStateException",
488 "Unable to retrieve AudioTrack pointer for stop()");
489 return;
490 }
491
492 lpTrack->stop();
493 }
494
495
496 // ----------------------------------------------------------------------------
497 static void
android_media_AudioTrack_pause(JNIEnv * env,jobject thiz)498 android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
499 {
500 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
501 if (lpTrack == NULL) {
502 jniThrowException(env, "java/lang/IllegalStateException",
503 "Unable to retrieve AudioTrack pointer for pause()");
504 return;
505 }
506
507 lpTrack->pause();
508 }
509
510
511 // ----------------------------------------------------------------------------
512 static void
android_media_AudioTrack_flush(JNIEnv * env,jobject thiz)513 android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
514 {
515 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
516 if (lpTrack == NULL) {
517 jniThrowException(env, "java/lang/IllegalStateException",
518 "Unable to retrieve AudioTrack pointer for flush()");
519 return;
520 }
521
522 lpTrack->flush();
523 }
524
525 // ----------------------------------------------------------------------------
526 static void
android_media_AudioTrack_set_volume(JNIEnv * env,jobject thiz,jfloat leftVol,jfloat rightVol)527 android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
528 {
529 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
530 if (lpTrack == NULL) {
531 jniThrowException(env, "java/lang/IllegalStateException",
532 "Unable to retrieve AudioTrack pointer for setVolume()");
533 return;
534 }
535
536 lpTrack->setVolume(leftVol, rightVol);
537 }
538
539 // ----------------------------------------------------------------------------
540
541 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioTrack_release(JNIEnv * env,jobject thiz)542 static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
543 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
544 if (lpTrack == NULL) {
545 return;
546 }
547 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
548
549 // delete the JNI data
550 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
551 thiz, javaAudioTrackFields.jniData);
552 // reset the native resources in the Java object so any attempt to access
553 // them after a call to release fails.
554 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
555
556 if (pJniStorage) {
557 Mutex::Autolock l(sLock);
558 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
559 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
560 while (lpCookie->busy) {
561 if (lpCookie->cond.waitRelative(sLock,
562 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
563 NO_ERROR) {
564 break;
565 }
566 }
567 sAudioTrackCallBackCookies.remove(lpCookie);
568 // delete global refs created in native_setup
569 env->DeleteGlobalRef(lpCookie->audioTrack_class);
570 env->DeleteGlobalRef(lpCookie->audioTrack_ref);
571 delete pJniStorage;
572 }
573 }
574
575
576 // ----------------------------------------------------------------------------
android_media_AudioTrack_finalize(JNIEnv * env,jobject thiz)577 static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) {
578 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
579 android_media_AudioTrack_release(env, thiz);
580 }
581
582 // overloaded JNI array helper functions (same as in android_media_AudioRecord)
583 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)584 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
585 return env->GetByteArrayElements(array, isCopy);
586 }
587
588 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)589 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
590 env->ReleaseByteArrayElements(array, elems, mode);
591 }
592
593 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)594 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
595 return env->GetShortArrayElements(array, isCopy);
596 }
597
598 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)599 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
600 env->ReleaseShortArrayElements(array, elems, mode);
601 }
602
603 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)604 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
605 return env->GetFloatArrayElements(array, isCopy);
606 }
607
608 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)609 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
610 env->ReleaseFloatArrayElements(array, elems, mode);
611 }
612
613 static inline
interpretWriteSizeError(ssize_t writeSize)614 jint interpretWriteSizeError(ssize_t writeSize) {
615 if (writeSize == WOULD_BLOCK) {
616 return (jint)0;
617 } else if (writeSize == NO_INIT) {
618 return AUDIO_JAVA_DEAD_OBJECT;
619 } else {
620 ALOGE("Error %zd during AudioTrack native read", writeSize);
621 return nativeToJavaStatus(writeSize);
622 }
623 }
624
625 // ----------------------------------------------------------------------------
626 template <typename T>
writeToTrack(const sp<AudioTrack> & track,jint audioFormat,const T * data,jint offsetInSamples,jint sizeInSamples,bool blocking)627 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
628 jint offsetInSamples, jint sizeInSamples, bool blocking) {
629 // give the data to the native AudioTrack object (the data starts at the offset)
630 ssize_t written = 0;
631 // regular write() or copy the data to the AudioTrack's shared memory?
632 size_t sizeInBytes = sizeInSamples * sizeof(T);
633 if (track->sharedBuffer() == 0) {
634 written = track->write(data + offsetInSamples, sizeInBytes, blocking);
635 // for compatibility with earlier behavior of write(), return 0 in this case
636 if (written == (ssize_t) WOULD_BLOCK) {
637 written = 0;
638 }
639 } else {
640 // writing to shared memory, check for capacity
641 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
642 sizeInBytes = track->sharedBuffer()->size();
643 }
644 memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
645 written = sizeInBytes;
646 }
647 if (written >= 0) {
648 return written / sizeof(T);
649 }
650 return interpretWriteSizeError(written);
651 }
652
653 // ----------------------------------------------------------------------------
654 template <typename T>
android_media_AudioTrack_writeArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jint javaAudioFormat,jboolean isWriteBlocking)655 static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
656 T javaAudioData,
657 jint offsetInSamples, jint sizeInSamples,
658 jint javaAudioFormat,
659 jboolean isWriteBlocking) {
660 //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
661 // offsetInSamples, sizeInSamples);
662 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
663 if (lpTrack == NULL) {
664 jniThrowException(env, "java/lang/IllegalStateException",
665 "Unable to retrieve AudioTrack pointer for write()");
666 return (jint)AUDIO_JAVA_INVALID_OPERATION;
667 }
668
669 if (javaAudioData == NULL) {
670 ALOGE("NULL java array of audio data to play");
671 return (jint)AUDIO_JAVA_BAD_VALUE;
672 }
673
674 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
675 // a way that it becomes much more efficient. When doing so, we will have to prevent the
676 // AudioSystem callback to be called while in critical section (in case of media server
677 // process crash for instance)
678
679 // get the pointer for the audio data from the java array
680 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
681 if (cAudioData == NULL) {
682 ALOGE("Error retrieving source of audio data to play");
683 return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
684 }
685
686 jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
687 offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
688
689 envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
690
691 //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
692 // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
693 return samplesWritten;
694 }
695
696 // ----------------------------------------------------------------------------
android_media_AudioTrack_write_native_bytes(JNIEnv * env,jobject thiz,jbyteArray javaBytes,jint byteOffset,jint sizeInBytes,jint javaAudioFormat,jboolean isWriteBlocking)697 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
698 jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
699 jint javaAudioFormat, jboolean isWriteBlocking) {
700 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
701 // offsetInBytes, sizeInBytes);
702 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
703 if (lpTrack == NULL) {
704 jniThrowException(env, "java/lang/IllegalStateException",
705 "Unable to retrieve AudioTrack pointer for write()");
706 return (jint)AUDIO_JAVA_INVALID_OPERATION;
707 }
708
709 ScopedBytesRO bytes(env, javaBytes);
710 if (bytes.get() == NULL) {
711 ALOGE("Error retrieving source of audio data to play, can't play");
712 return (jint)AUDIO_JAVA_BAD_VALUE;
713 }
714
715 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
716 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
717
718 return written;
719 }
720
721 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_buffer_size_frames(JNIEnv * env,jobject thiz)722 static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env, jobject thiz) {
723 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
724 if (lpTrack == NULL) {
725 jniThrowException(env, "java/lang/IllegalStateException",
726 "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()");
727 return (jint)AUDIO_JAVA_ERROR;
728 }
729
730 ssize_t result = lpTrack->getBufferSizeInFrames();
731 if (result < 0) {
732 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
733 "Internal error detected in getBufferSizeInFrames() = %zd", result);
734 return (jint)AUDIO_JAVA_ERROR;
735 }
736 return (jint)result;
737 }
738
739 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_buffer_size_frames(JNIEnv * env,jobject thiz,jint bufferSizeInFrames)740 static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env,
741 jobject thiz, jint bufferSizeInFrames) {
742 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
743 if (lpTrack == NULL) {
744 jniThrowException(env, "java/lang/IllegalStateException",
745 "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()");
746 return (jint)AUDIO_JAVA_ERROR;
747 }
748 // Value will be coerced into the valid range.
749 // But internal values are unsigned, size_t, so we need to clip
750 // against zero here where it is signed.
751 if (bufferSizeInFrames < 0) {
752 bufferSizeInFrames = 0;
753 }
754 ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames);
755 if (result < 0) {
756 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
757 "Internal error detected in setBufferSizeInFrames() = %zd", result);
758 return (jint)AUDIO_JAVA_ERROR;
759 }
760 return (jint)result;
761 }
762
763 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv * env,jobject thiz)764 static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env, jobject thiz) {
765 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
766 if (lpTrack == NULL) {
767 jniThrowException(env, "java/lang/IllegalStateException",
768 "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()");
769 return (jint)AUDIO_JAVA_ERROR;
770 }
771
772 return lpTrack->frameCount();
773 }
774
775 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_rate(JNIEnv * env,jobject thiz,jint sampleRateInHz)776 static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz,
777 jint sampleRateInHz) {
778 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
779 if (lpTrack == NULL) {
780 jniThrowException(env, "java/lang/IllegalStateException",
781 "Unable to retrieve AudioTrack pointer for setSampleRate()");
782 return (jint)AUDIO_JAVA_ERROR;
783 }
784 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
785 }
786
787
788 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_rate(JNIEnv * env,jobject thiz)789 static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) {
790 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
791 if (lpTrack == NULL) {
792 jniThrowException(env, "java/lang/IllegalStateException",
793 "Unable to retrieve AudioTrack pointer for getSampleRate()");
794 return (jint)AUDIO_JAVA_ERROR;
795 }
796 return (jint) lpTrack->getSampleRate();
797 }
798
799
800 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_playback_params(JNIEnv * env,jobject thiz,jobject params)801 static void android_media_AudioTrack_set_playback_params(JNIEnv *env, jobject thiz,
802 jobject params) {
803 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
804 if (lpTrack == NULL) {
805 jniThrowException(env, "java/lang/IllegalStateException",
806 "AudioTrack not initialized");
807 return;
808 }
809
810 PlaybackParams pbp;
811 pbp.fillFromJobject(env, gPlaybackParamsFields, params);
812
813 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
814 pbp.speedSet, pbp.audioRate.mSpeed,
815 pbp.pitchSet, pbp.audioRate.mPitch,
816 pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
817 pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
818
819 // to simulate partially set params, we do a read-modify-write.
820 // TODO: pass in the valid set mask into AudioTrack.
821 AudioPlaybackRate rate = lpTrack->getPlaybackRate();
822 bool updatedRate = false;
823 if (pbp.speedSet) {
824 rate.mSpeed = pbp.audioRate.mSpeed;
825 updatedRate = true;
826 }
827 if (pbp.pitchSet) {
828 rate.mPitch = pbp.audioRate.mPitch;
829 updatedRate = true;
830 }
831 if (pbp.audioFallbackModeSet) {
832 rate.mFallbackMode = pbp.audioRate.mFallbackMode;
833 updatedRate = true;
834 }
835 if (pbp.audioStretchModeSet) {
836 rate.mStretchMode = pbp.audioRate.mStretchMode;
837 updatedRate = true;
838 }
839 if (updatedRate) {
840 if (lpTrack->setPlaybackRate(rate) != OK) {
841 jniThrowException(env, "java/lang/IllegalArgumentException",
842 "arguments out of range");
843 }
844 }
845 }
846
847
848 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_playback_params(JNIEnv * env,jobject thiz,jobject params)849 static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env, jobject thiz,
850 jobject params) {
851 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
852 if (lpTrack == NULL) {
853 jniThrowException(env, "java/lang/IllegalStateException",
854 "AudioTrack not initialized");
855 return NULL;
856 }
857
858 PlaybackParams pbs;
859 pbs.audioRate = lpTrack->getPlaybackRate();
860 pbs.speedSet = true;
861 pbs.pitchSet = true;
862 pbs.audioFallbackModeSet = true;
863 pbs.audioStretchModeSet = true;
864 return pbs.asJobject(env, gPlaybackParamsFields);
865 }
866
867
868 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)869 static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz,
870 jint markerPos) {
871 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
872 if (lpTrack == NULL) {
873 jniThrowException(env, "java/lang/IllegalStateException",
874 "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
875 return (jint)AUDIO_JAVA_ERROR;
876 }
877 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
878 }
879
880
881 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_marker_pos(JNIEnv * env,jobject thiz)882 static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) {
883 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
884 uint32_t markerPos = 0;
885
886 if (lpTrack == NULL) {
887 jniThrowException(env, "java/lang/IllegalStateException",
888 "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
889 return (jint)AUDIO_JAVA_ERROR;
890 }
891 lpTrack->getMarkerPosition(&markerPos);
892 return (jint)markerPos;
893 }
894
895
896 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)897 static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz,
898 jint period) {
899 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
900 if (lpTrack == NULL) {
901 jniThrowException(env, "java/lang/IllegalStateException",
902 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
903 return (jint)AUDIO_JAVA_ERROR;
904 }
905 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
906 }
907
908
909 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_pos_update_period(JNIEnv * env,jobject thiz)910 static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) {
911 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
912 uint32_t period = 0;
913
914 if (lpTrack == NULL) {
915 jniThrowException(env, "java/lang/IllegalStateException",
916 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
917 return (jint)AUDIO_JAVA_ERROR;
918 }
919 lpTrack->getPositionUpdatePeriod(&period);
920 return (jint)period;
921 }
922
923
924 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_position(JNIEnv * env,jobject thiz,jint position)925 static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz,
926 jint position) {
927 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
928 if (lpTrack == NULL) {
929 jniThrowException(env, "java/lang/IllegalStateException",
930 "Unable to retrieve AudioTrack pointer for setPosition()");
931 return (jint)AUDIO_JAVA_ERROR;
932 }
933 return nativeToJavaStatus( lpTrack->setPosition(position) );
934 }
935
936
937 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_position(JNIEnv * env,jobject thiz)938 static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) {
939 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
940 uint32_t position = 0;
941
942 if (lpTrack == NULL) {
943 jniThrowException(env, "java/lang/IllegalStateException",
944 "Unable to retrieve AudioTrack pointer for getPosition()");
945 return (jint)AUDIO_JAVA_ERROR;
946 }
947 lpTrack->getPosition(&position);
948 return (jint)position;
949 }
950
951
952 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_latency(JNIEnv * env,jobject thiz)953 static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) {
954 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
955
956 if (lpTrack == NULL) {
957 jniThrowException(env, "java/lang/IllegalStateException",
958 "Unable to retrieve AudioTrack pointer for latency()");
959 return (jint)AUDIO_JAVA_ERROR;
960 }
961 return (jint)lpTrack->latency();
962 }
963
964 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_underrun_count(JNIEnv * env,jobject thiz)965 static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject thiz) {
966 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
967
968 if (lpTrack == NULL) {
969 jniThrowException(env, "java/lang/IllegalStateException",
970 "Unable to retrieve AudioTrack pointer for getUnderrunCount()");
971 return (jint)AUDIO_JAVA_ERROR;
972 }
973 return (jint)lpTrack->getUnderrunCount();
974 }
975
976 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_flags(JNIEnv * env,jobject thiz)977 static jint android_media_AudioTrack_get_flags(JNIEnv *env, jobject thiz) {
978 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
979
980 if (lpTrack == NULL) {
981 jniThrowException(env, "java/lang/IllegalStateException",
982 "Unable to retrieve AudioTrack pointer for getFlags()");
983 return (jint)AUDIO_JAVA_ERROR;
984 }
985 return (jint)lpTrack->getFlags();
986 }
987
988 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_timestamp(JNIEnv * env,jobject thiz,jlongArray jTimestamp)989 static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) {
990 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
991
992 if (lpTrack == NULL) {
993 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
994 return (jint)AUDIO_JAVA_ERROR;
995 }
996 AudioTimestamp timestamp;
997 status_t status = lpTrack->getTimestamp(timestamp);
998 if (status == OK) {
999 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
1000 if (nTimestamp == NULL) {
1001 ALOGE("Unable to get array for getTimestamp()");
1002 return (jint)AUDIO_JAVA_ERROR;
1003 }
1004 nTimestamp[0] = (jlong) timestamp.mPosition;
1005 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
1006 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
1007 }
1008 return (jint) nativeToJavaStatus(status);
1009 }
1010
1011
1012 // ----------------------------------------------------------------------------
android_media_AudioTrack_set_loop(JNIEnv * env,jobject thiz,jint loopStart,jint loopEnd,jint loopCount)1013 static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz,
1014 jint loopStart, jint loopEnd, jint loopCount) {
1015 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1016 if (lpTrack == NULL) {
1017 jniThrowException(env, "java/lang/IllegalStateException",
1018 "Unable to retrieve AudioTrack pointer for setLoop()");
1019 return (jint)AUDIO_JAVA_ERROR;
1020 }
1021 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
1022 }
1023
1024
1025 // ----------------------------------------------------------------------------
android_media_AudioTrack_reload(JNIEnv * env,jobject thiz)1026 static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) {
1027 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1028 if (lpTrack == NULL) {
1029 jniThrowException(env, "java/lang/IllegalStateException",
1030 "Unable to retrieve AudioTrack pointer for reload()");
1031 return (jint)AUDIO_JAVA_ERROR;
1032 }
1033 return nativeToJavaStatus( lpTrack->reload() );
1034 }
1035
1036
1037 // ----------------------------------------------------------------------------
android_media_AudioTrack_get_output_sample_rate(JNIEnv * env,jobject thiz,jint javaStreamType)1038 static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz,
1039 jint javaStreamType) {
1040 uint32_t afSamplingRate;
1041 // convert the stream type from Java to native value
1042 // FIXME: code duplication with android_media_AudioTrack_setup()
1043 audio_stream_type_t nativeStreamType;
1044 switch (javaStreamType) {
1045 case AUDIO_STREAM_VOICE_CALL:
1046 case AUDIO_STREAM_SYSTEM:
1047 case AUDIO_STREAM_RING:
1048 case AUDIO_STREAM_MUSIC:
1049 case AUDIO_STREAM_ALARM:
1050 case AUDIO_STREAM_NOTIFICATION:
1051 case AUDIO_STREAM_BLUETOOTH_SCO:
1052 case AUDIO_STREAM_DTMF:
1053 nativeStreamType = (audio_stream_type_t) javaStreamType;
1054 break;
1055 default:
1056 nativeStreamType = AUDIO_STREAM_DEFAULT;
1057 break;
1058 }
1059
1060 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
1061 if (status != NO_ERROR) {
1062 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
1063 "in AudioTrack JNI", status, nativeStreamType);
1064 return DEFAULT_OUTPUT_SAMPLE_RATE;
1065 } else {
1066 return afSamplingRate;
1067 }
1068 }
1069
1070
1071 // ----------------------------------------------------------------------------
1072 // returns the minimum required size for the successful creation of a streaming AudioTrack
1073 // returns -1 if there was an error querying the hardware.
android_media_AudioTrack_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)1074 static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
1075 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
1076
1077 size_t frameCount;
1078 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
1079 sampleRateInHertz);
1080 if (status != NO_ERROR) {
1081 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
1082 sampleRateInHertz, status);
1083 return -1;
1084 }
1085 const audio_format_t format = audioFormatToNative(audioFormat);
1086 if (audio_has_proportional_frames(format)) {
1087 const size_t bytesPerSample = audio_bytes_per_sample(format);
1088 return frameCount * channelCount * bytesPerSample;
1089 } else {
1090 return frameCount;
1091 }
1092 }
1093
1094 // ----------------------------------------------------------------------------
1095 static jint
android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv * env,jobject thiz,jfloat level)1096 android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
1097 {
1098 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1099 if (lpTrack == NULL ) {
1100 jniThrowException(env, "java/lang/IllegalStateException",
1101 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
1102 return -1;
1103 }
1104
1105 status_t status = lpTrack->setAuxEffectSendLevel(level);
1106 if (status != NO_ERROR) {
1107 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
1108 level, status);
1109 }
1110 return (jint) status;
1111 }
1112
1113 // ----------------------------------------------------------------------------
android_media_AudioTrack_attachAuxEffect(JNIEnv * env,jobject thiz,jint effectId)1114 static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz,
1115 jint effectId) {
1116 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1117 if (lpTrack == NULL) {
1118 jniThrowException(env, "java/lang/IllegalStateException",
1119 "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
1120 return (jint)AUDIO_JAVA_ERROR;
1121 }
1122 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
1123 }
1124
android_media_AudioTrack_setOutputDevice(JNIEnv * env,jobject thiz,jint device_id)1125 static jboolean android_media_AudioTrack_setOutputDevice(
1126 JNIEnv *env, jobject thiz, jint device_id) {
1127
1128 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1129 if (lpTrack == 0) {
1130 return false;
1131 }
1132 return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1133 }
1134
android_media_AudioTrack_getRoutedDeviceId(JNIEnv * env,jobject thiz)1135 static jint android_media_AudioTrack_getRoutedDeviceId(
1136 JNIEnv *env, jobject thiz) {
1137
1138 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1139 if (lpTrack == NULL) {
1140 return 0;
1141 }
1142 return (jint)lpTrack->getRoutedDeviceId();
1143 }
1144
android_media_AudioTrack_enableDeviceCallback(JNIEnv * env,jobject thiz)1145 static void android_media_AudioTrack_enableDeviceCallback(
1146 JNIEnv *env, jobject thiz) {
1147
1148 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1149 if (lpTrack == NULL) {
1150 return;
1151 }
1152 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1153 thiz, javaAudioTrackFields.jniData);
1154 if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1155 return;
1156 }
1157 pJniStorage->mDeviceCallback =
1158 new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1159 javaAudioTrackFields.postNativeEventInJava);
1160 lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1161 }
1162
android_media_AudioTrack_disableDeviceCallback(JNIEnv * env,jobject thiz)1163 static void android_media_AudioTrack_disableDeviceCallback(
1164 JNIEnv *env, jobject thiz) {
1165
1166 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1167 if (lpTrack == NULL) {
1168 return;
1169 }
1170 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1171 thiz, javaAudioTrackFields.jniData);
1172 if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1173 return;
1174 }
1175 lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1176 pJniStorage->mDeviceCallback.clear();
1177 }
1178
android_media_AudioTrack_get_FCC_8(JNIEnv * env,jobject thiz)1179 static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
1180 return FCC_8;
1181 }
1182
1183 // Pass through the arguments to the AudioFlinger track implementation.
android_media_AudioTrack_apply_volume_shaper(JNIEnv * env,jobject thiz,jobject jconfig,jobject joperation)1184 static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
1185 jobject jconfig, jobject joperation) {
1186 // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
1187 const int VOLUME_SHAPER_INVALID_OPERATION = -38;
1188
1189 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1190 if (lpTrack == nullptr) {
1191 return (jint)VOLUME_SHAPER_INVALID_OPERATION;
1192 }
1193
1194 sp<VolumeShaper::Configuration> configuration;
1195 sp<VolumeShaper::Operation> operation;
1196 if (jconfig != nullptr) {
1197 configuration = VolumeShaperHelper::convertJobjectToConfiguration(
1198 env, gVolumeShaperFields, jconfig);
1199 ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
1200 }
1201 if (joperation != nullptr) {
1202 operation = VolumeShaperHelper::convertJobjectToOperation(
1203 env, gVolumeShaperFields, joperation);
1204 ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
1205 }
1206 VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation);
1207 if (status == INVALID_OPERATION) {
1208 status = VOLUME_SHAPER_INVALID_OPERATION;
1209 }
1210 return (jint)status; // if status < 0 an error, else a VolumeShaper id
1211 }
1212
1213 // Pass through the arguments to the AudioFlinger track implementation.
android_media_AudioTrack_get_volume_shaper_state(JNIEnv * env,jobject thiz,jint id)1214 static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz,
1215 jint id) {
1216 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1217 if (lpTrack == nullptr) {
1218 return (jobject)nullptr;
1219 }
1220
1221 sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id);
1222 if (state.get() == nullptr) {
1223 return (jobject)nullptr;
1224 }
1225 return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
1226 }
1227
1228 // ----------------------------------------------------------------------------
1229 // ----------------------------------------------------------------------------
1230 static const JNINativeMethod gMethods[] = {
1231 // name, signature, funcPtr
1232 {"native_start", "()V", (void *)android_media_AudioTrack_start},
1233 {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
1234 {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
1235 {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
1236 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
1237 (void *)android_media_AudioTrack_setup},
1238 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
1239 {"native_release", "()V", (void *)android_media_AudioTrack_release},
1240 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
1241 {"native_write_native_bytes",
1242 "(Ljava/lang/Object;IIIZ)I",
1243 (void *)android_media_AudioTrack_write_native_bytes},
1244 {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1245 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
1246 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
1247 {"native_get_buffer_size_frames",
1248 "()I", (void *)android_media_AudioTrack_get_buffer_size_frames},
1249 {"native_set_buffer_size_frames",
1250 "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames},
1251 {"native_get_buffer_capacity_frames",
1252 "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames},
1253 {"native_set_playback_rate",
1254 "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
1255 {"native_get_playback_rate",
1256 "()I", (void *)android_media_AudioTrack_get_playback_rate},
1257 {"native_set_playback_params",
1258 "(Landroid/media/PlaybackParams;)V",
1259 (void *)android_media_AudioTrack_set_playback_params},
1260 {"native_get_playback_params",
1261 "()Landroid/media/PlaybackParams;",
1262 (void *)android_media_AudioTrack_get_playback_params},
1263 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos},
1264 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos},
1265 {"native_set_pos_update_period",
1266 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period},
1267 {"native_get_pos_update_period",
1268 "()I", (void *)android_media_AudioTrack_get_pos_update_period},
1269 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
1270 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
1271 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
1272 {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
1273 {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
1274 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
1275 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
1276 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
1277 {"native_get_output_sample_rate",
1278 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate},
1279 {"native_get_min_buff_size",
1280 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
1281 {"native_setAuxEffectSendLevel",
1282 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel},
1283 {"native_attachAuxEffect",
1284 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
1285 {"native_setOutputDevice", "(I)Z",
1286 (void *)android_media_AudioTrack_setOutputDevice},
1287 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1288 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1289 {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
1290 {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8},
1291 {"native_applyVolumeShaper",
1292 "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
1293 (void *)android_media_AudioTrack_apply_volume_shaper},
1294 {"native_getVolumeShaperState",
1295 "(I)Landroid/media/VolumeShaper$State;",
1296 (void *)android_media_AudioTrack_get_volume_shaper_state},
1297 };
1298
1299
1300 // field names found in android/media/AudioTrack.java
1301 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
1302 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
1303 #define JAVA_JNIDATA_FIELD_NAME "mJniData"
1304 #define JAVA_STREAMTYPE_FIELD_NAME "mStreamType"
1305
1306 // ----------------------------------------------------------------------------
1307 // preconditions:
1308 // theClass is valid
android_media_getIntConstantFromClass(JNIEnv * pEnv,jclass theClass,const char * className,const char * constName,int * constVal)1309 bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1310 const char* constName, int* constVal) {
1311 jfieldID javaConst = NULL;
1312 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1313 if (javaConst != NULL) {
1314 *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1315 return true;
1316 } else {
1317 ALOGE("Can't find %s.%s", className, constName);
1318 return false;
1319 }
1320 }
1321
1322
1323 // ----------------------------------------------------------------------------
register_android_media_AudioTrack(JNIEnv * env)1324 int register_android_media_AudioTrack(JNIEnv *env)
1325 {
1326 // must be first
1327 int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1328
1329 javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1330 javaAudioTrackFields.postNativeEventInJava = NULL;
1331
1332 // Get the AudioTrack class
1333 jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
1334
1335 // Get the postEvent method
1336 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1337 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1338 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
1339
1340 // Get the variables fields
1341 // nativeTrackInJavaObj
1342 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1343 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
1344 // jniData
1345 javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1346 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
1347 // fieldStreamType
1348 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1349 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
1350
1351 env->DeleteLocalRef(audioTrackClass);
1352
1353 // Get the AudioAttributes class and fields
1354 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
1355 javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I");
1356 javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env,
1357 audioAttrClass, "mContentType", "I");
1358 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
1359 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
1360 audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
1361
1362 env->DeleteLocalRef(audioAttrClass);
1363
1364 // initialize PlaybackParams field info
1365 gPlaybackParamsFields.init(env);
1366
1367 gVolumeShaperFields.init(env);
1368 return res;
1369 }
1370
1371
1372 // ----------------------------------------------------------------------------
1373