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