blob: afbc579136880955ae59e63371a603b684da0a90 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Wei Jia071a8b72015-03-09 16:38:25 -070020#include "android_media_AudioTrack.h"
21
Steven Moreland2279b252017-07-19 09:50:45 -070022#include <nativehelper/JNIHelp.h>
23#include <nativehelper/JniConstants.h>
Andreas Gampeed6b9df2014-11-20 22:02:20 -080024#include "core_jni_helpers.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
Steven Moreland2279b252017-07-19 09:50:45 -070026#include <nativehelper/ScopedBytes.h>
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -080027
Glenn Kastenc81d31c2012-03-13 14:46:23 -070028#include <utils/Log.h>
29#include <media/AudioSystem.h>
30#include <media/AudioTrack.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
Mathias Agopian07952722009-05-19 19:08:10 -070032#include <binder/MemoryHeapBase.h>
33#include <binder/MemoryBase.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034
Glenn Kastenfe834d32014-01-08 14:49:08 -080035#include "android_media_AudioFormat.h"
Eric Laurentbc11a692014-05-16 12:19:25 -070036#include "android_media_AudioErrors.h"
Ray Essick510225b2018-01-24 14:27:16 -080037#include "android_media_MediaMetricsJNI.h"
Wei Jia2d61e2b2015-05-08 15:23:28 -070038#include "android_media_PlaybackParams.h"
Eric Laurent4bcdba82015-05-01 11:37:49 -070039#include "android_media_DeviceCallback.h"
Andy Hung035d4ec2017-01-24 13:45:02 -080040#include "android_media_VolumeShaper.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
Ben Wagner85ea32b2016-03-16 17:10:42 -040042#include <cinttypes>
43
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044// ----------------------------------------------------------------------------
45
46using namespace android;
47
Ivan Lozano330d8762017-08-08 12:51:06 -070048using ::android::media::VolumeShaper;
49
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050// ----------------------------------------------------------------------------
51static const char* const kClassPathName = "android/media/AudioTrack";
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070052static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070054struct audio_track_fields_t {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 jmethodID postNativeEventInJava; //... event post callback method
Eric Laurent83b36852009-07-28 07:49:22 -070057 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070059 jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060};
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070061struct audio_attributes_fields_t {
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070062 jfieldID fieldUsage; // AudioAttributes.mUsage
63 jfieldID fieldContentType; // AudioAttributes.mContentType
64 jfieldID fieldFlags; // AudioAttributes.mFlags
65 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070066};
67static audio_track_fields_t javaAudioTrackFields;
68static audio_attributes_fields_t javaAudioAttrFields;
Wei Jia2d61e2b2015-05-08 15:23:28 -070069static PlaybackParams::fields_t gPlaybackParamsFields;
Andy Hung035d4ec2017-01-24 13:45:02 -080070static VolumeShaperHelper::fields_t gVolumeShaperFields;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
72struct audiotrack_callback_cookie {
73 jclass audioTrack_class;
74 jobject audioTrack_ref;
Eric Laurent532bc1c2012-04-20 12:45:03 -070075 bool busy;
76 Condition cond;
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -080077 bool isOffload;
Eric Laurent532bc1c2012-04-20 12:45:03 -070078};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Glenn Kasten3d301cb2012-01-16 14:46:54 -080080// keep these values in sync with AudioTrack.java
81#define MODE_STATIC 0
82#define MODE_STREAM 1
83
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084// ----------------------------------------------------------------------------
85class AudioTrackJniStorage {
86 public:
87 sp<MemoryHeapBase> mMemHeap;
88 sp<MemoryBase> mMemBase;
89 audiotrack_callback_cookie mCallbackData;
Eric Laurent4bcdba82015-05-01 11:37:49 -070090 sp<JNIDeviceCallback> mDeviceCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
92 AudioTrackJniStorage() {
Jean-Michel Trivi8a149682009-07-15 18:31:11 -070093 mCallbackData.audioTrack_class = 0;
94 mCallbackData.audioTrack_ref = 0;
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -080095 mCallbackData.isOffload = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 }
97
98 ~AudioTrackJniStorage() {
99 mMemBase.clear();
100 mMemHeap.clear();
101 }
102
103 bool allocSharedMem(int sizeInBytes) {
104 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
105 if (mMemHeap->getHeapID() < 0) {
106 return false;
107 }
108 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
109 return true;
110 }
111};
112
Eric Laurent532bc1c2012-04-20 12:45:03 -0700113static Mutex sLock;
114static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116// ----------------------------------------------------------------------------
117#define DEFAULT_OUTPUT_SAMPLE_RATE 44100
118
Chih-Hung Hsieh0ca16ef2016-05-19 15:14:54 -0700119#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM (-16)
120#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK (-17)
121#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT (-18)
122#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19)
123#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125// ----------------------------------------------------------------------------
Glenn Kastene46a86f2011-06-01 15:20:35 -0700126static void audioCallback(int event, void* user, void *info) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700127
128 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
129 {
130 Mutex::Autolock l(sLock);
131 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
132 return;
133 }
134 callbackInfo->busy = true;
135 }
136
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700137 switch (event) {
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800138 // Offload only events
139 case AudioTrack::EVENT_STREAM_END:
140 case AudioTrack::EVENT_MORE_DATA:
141 // a.k.a. tear down
142 case AudioTrack::EVENT_NEW_IAUDIOTRACK:
143 if (callbackInfo->isOffload) {
144 JNIEnv *env = AndroidRuntime::getJNIEnv();
145 if (user != NULL && env != NULL) {
146 env->CallStaticVoidMethod(
147 callbackInfo->audioTrack_class,
148 javaAudioTrackFields.postNativeEventInJava,
149 callbackInfo->audioTrack_ref, event, 0,0, NULL);
150 if (env->ExceptionCheck()) {
151 env->ExceptionDescribe();
152 env->ExceptionClear();
153 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700155 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800157 // PCM and offload events
158 case AudioTrack::EVENT_MARKER:
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700159 case AudioTrack::EVENT_NEW_POS: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -0700161 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 env->CallStaticVoidMethod(
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800163 callbackInfo->audioTrack_class,
164 javaAudioTrackFields.postNativeEventInJava,
165 callbackInfo->audioTrack_ref, event, 0,0, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 if (env->ExceptionCheck()) {
167 env->ExceptionDescribe();
168 env->ExceptionClear();
169 }
170 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700171 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700173
Eric Laurent532bc1c2012-04-20 12:45:03 -0700174 {
175 Mutex::Autolock l(sLock);
176 callbackInfo->busy = false;
177 callbackInfo->cond.broadcast();
178 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179}
180
181
182// ----------------------------------------------------------------------------
Eric Laurent532bc1c2012-04-20 12:45:03 -0700183static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
184{
185 Mutex::Autolock l(sLock);
186 AudioTrack* const at =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000187 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700188 return sp<AudioTrack>(at);
189}
190
191static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
192{
193 Mutex::Autolock l(sLock);
194 sp<AudioTrack> old =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000195 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700196 if (at.get()) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800197 at->incStrong((void*)setAudioTrack);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700198 }
199 if (old != 0) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800200 old->decStrong((void*)setAudioTrack);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700201 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000202 env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700203 return old;
204}
Wei Jia071a8b72015-03-09 16:38:25 -0700205
206// ----------------------------------------------------------------------------
207sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) {
208 return getAudioTrack(env, audioTrackObj);
209}
210
Eric Laurent532bc1c2012-04-20 12:45:03 -0700211// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000212static jint
Paul McLean9b09e532016-01-26 14:43:35 -0700213android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800214 jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
Paul McLean9b09e532016-01-26 14:43:35 -0700215 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800216 jlong nativeAudioTrack, jboolean offload) {
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700217
Paul McLean9b09e532016-01-26 14:43:35 -0700218 ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
Ben Wagner85ea32b2016-03-16 17:10:42 -0400219 "nativeAudioTrack=0x%" PRIX64,
Paul McLean9b09e532016-01-26 14:43:35 -0700220 jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
221 nativeAudioTrack);
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800222
Paul McLean9b09e532016-01-26 14:43:35 -0700223 sp<AudioTrack> lpTrack = 0;
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700224
225 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000226 ALOGE("Error creating AudioTrack: invalid session ID pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700227 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent619346f2010-06-21 09:27:30 -0700228 }
229
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700230 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
231 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000232 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700233 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700234 }
Glenn Kasten33b84042016-03-08 12:02:55 -0800235 audio_session_t sessionId = (audio_session_t) nSession[0];
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700236 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
237 nSession = NULL;
238
Paul McLean9b09e532016-01-26 14:43:35 -0700239 AudioTrackJniStorage* lpJniStorage = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700240
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700241 audio_attributes_t *paa = NULL;
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700242
Paul McLean9b09e532016-01-26 14:43:35 -0700243 jclass clazz = env->GetObjectClass(thiz);
244 if (clazz == NULL) {
245 ALOGE("Can't find %s when setting up callback.", kClassPathName);
246 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
247 }
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700248
Paul McLean9b09e532016-01-26 14:43:35 -0700249 // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
250 if (nativeAudioTrack == 0) {
251 if (jaa == 0) {
252 ALOGE("Error creating AudioTrack: invalid audio attributes");
253 return (jint) AUDIO_JAVA_ERROR;
254 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700255
Paul McLean9b09e532016-01-26 14:43:35 -0700256 if (jSampleRate == 0) {
257 ALOGE("Error creating AudioTrack: invalid sample rates");
258 return (jint) AUDIO_JAVA_ERROR;
259 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260
Paul McLean9b09e532016-01-26 14:43:35 -0700261 int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
262 int sampleRateInHertz = sampleRates[0];
263 env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700264
Paul McLean9b09e532016-01-26 14:43:35 -0700265 // Invalid channel representations are caught by !audio_is_output_channel() below.
266 audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
267 channelPositionMask, channelIndexMask);
268 if (!audio_is_output_channel(nativeChannelMask)) {
269 ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
270 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
271 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700272
Paul McLean9b09e532016-01-26 14:43:35 -0700273 uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
274
275 // check the format.
276 // This function was called from Java, so we compare the format against the Java constants
277 audio_format_t format = audioFormatToNative(audioFormat);
278 if (format == AUDIO_FORMAT_INVALID) {
279 ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
280 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
281 }
282
283 // compute the frame count
284 size_t frameCount;
285 if (audio_is_linear_pcm(format)) {
286 const size_t bytesPerSample = audio_bytes_per_sample(format);
287 frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
288 } else {
289 frameCount = buffSizeInBytes;
290 }
291
292 // create the native AudioTrack object
293 lpTrack = new AudioTrack();
294
295 // read the AudioAttributes values
296 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
297 const jstring jtags =
298 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
299 const char* tags = env->GetStringUTFChars(jtags, NULL);
300 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
301 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
302 env->ReleaseStringUTFChars(jtags, tags);
303 paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
304 paa->content_type =
305 (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
306 paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
307
308 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
309 paa->usage, paa->content_type, paa->flags, paa->tags);
310
311 // initialize the callback information:
312 // this data will be passed with every AudioTrack callback
313 lpJniStorage = new AudioTrackJniStorage();
314 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
315 // we use a weak reference so the AudioTrack object can be garbage collected.
316 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800317 lpJniStorage->mCallbackData.isOffload = offload;
Paul McLean9b09e532016-01-26 14:43:35 -0700318 lpJniStorage->mCallbackData.busy = false;
319
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800320 audio_offload_info_t offloadInfo;
321 if (offload) {
322 offloadInfo = AUDIO_INFO_INITIALIZER;
323 offloadInfo.format = format;
324 offloadInfo.sample_rate = sampleRateInHertz;
325 offloadInfo.channel_mask = nativeChannelMask;
326 offloadInfo.has_video = false;
327 offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
328 }
329
Paul McLean9b09e532016-01-26 14:43:35 -0700330 // initialize the native AudioTrack object
331 status_t status = NO_ERROR;
332 switch (memoryMode) {
333 case MODE_STREAM:
334
335 status = lpTrack->set(
336 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
337 sampleRateInHertz,
338 format,// word length, PCM
339 nativeChannelMask,
340 frameCount,
341 AUDIO_OUTPUT_FLAG_NONE,
342 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
343 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
344 0,// shared mem
345 true,// thread can call Java
346 sessionId,// audio session ID
347 AudioTrack::TRANSFER_SYNC,
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800348 offload ? &offloadInfo : NULL,
Paul McLean9b09e532016-01-26 14:43:35 -0700349 -1, -1, // default uid, pid values
350 paa);
351 break;
352
353 case MODE_STATIC:
354 // AudioTrack is using shared memory
355
356 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
357 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
358 goto native_init_failure;
359 }
360
361 status = lpTrack->set(
362 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
363 sampleRateInHertz,
364 format,// word length, PCM
365 nativeChannelMask,
366 frameCount,
367 AUDIO_OUTPUT_FLAG_NONE,
368 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
369 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
370 lpJniStorage->mMemBase,// shared mem
371 true,// thread can call Java
372 sessionId,// audio session ID
373 AudioTrack::TRANSFER_SHARED,
374 NULL, // default offloadInfo
375 -1, -1, // default uid, pid values
376 paa);
377 break;
378
379 default:
380 ALOGE("Unknown mode %d", memoryMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 goto native_init_failure;
382 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700383
Paul McLean9b09e532016-01-26 14:43:35 -0700384 if (status != NO_ERROR) {
385 ALOGE("Error %d initializing AudioTrack", status);
386 goto native_init_failure;
387 }
388 } else { // end if (nativeAudioTrack == 0)
389 lpTrack = (AudioTrack*)nativeAudioTrack;
390 // TODO: We need to find out which members of the Java AudioTrack might
391 // need to be initialized from the Native AudioTrack
392 // these are directly returned from getters:
393 // mSampleRate
394 // mAudioFormat
395 // mStreamType
396 // mChannelConfiguration
397 // mChannelCount
398 // mState (?)
399 // mPlayState (?)
400 // these may be used internally (Java AudioTrack.audioParamCheck():
401 // mChannelMask
402 // mChannelIndexMask
403 // mDataLoadMode
Glenn Kasten3d301cb2012-01-16 14:46:54 -0800404
Paul McLean9b09e532016-01-26 14:43:35 -0700405 // initialize the callback information:
406 // this data will be passed with every AudioTrack callback
407 lpJniStorage = new AudioTrackJniStorage();
408 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
409 // we use a weak reference so the AudioTrack object can be garbage collected.
410 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
411 lpJniStorage->mCallbackData.busy = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 }
413
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700414 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
415 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000416 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700417 goto native_init_failure;
418 }
Eric Laurent619346f2010-06-21 09:27:30 -0700419 // read the audio session ID back from AudioTrack in case we create a new session
420 nSession[0] = lpTrack->getSessionId();
Eric Laurent619346f2010-06-21 09:27:30 -0700421 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
422 nSession = NULL;
423
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800424 {
425 const jint elements[1] = { (jint) lpTrack->getSampleRate() };
426 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
427 }
428
Eric Laurent532bc1c2012-04-20 12:45:03 -0700429 { // scope for the lock
430 Mutex::Autolock l(sLock);
431 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
432 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700433 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 // of the Java object (in mNativeTrackInJavaObj)
Eric Laurent532bc1c2012-04-20 12:45:03 -0700435 setAudioTrack(env, thiz, lpTrack);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700436
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 // save the JNI resources so we can free them later
Ashok Bhat075e9a12014-01-06 13:45:09 +0000438 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
439 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700441 // since we had audio attributes, the stream type was derived from them during the
442 // creation of the native AudioTrack: push the same value to the Java object
443 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
Paul McLean9b09e532016-01-26 14:43:35 -0700444 if (paa != NULL) {
445 // audio attributes were copied in AudioTrack creation
446 free(paa);
447 paa = NULL;
448 }
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700449
450
Eric Laurentbc11a692014-05-16 12:19:25 -0700451 return (jint) AUDIO_JAVA_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 // failures:
454native_init_failure:
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700455 if (paa != NULL) {
456 free(paa);
457 }
Eric Laurent619346f2010-06-21 09:27:30 -0700458 if (nSession != NULL) {
459 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
460 }
Jean-Michel Trivi8a149682009-07-15 18:31:11 -0700461 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
462 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 delete lpJniStorage;
Ashok Bhat075e9a12014-01-06 13:45:09 +0000464 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700465
Glenn Kasten14d226a2015-05-18 13:53:39 -0700466 // lpTrack goes out of scope, so reference count drops to zero
Ashok Bhat075e9a12014-01-06 13:45:09 +0000467 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468}
469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470// ----------------------------------------------------------------------------
471static void
472android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
473{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700474 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
475 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 jniThrowException(env, "java/lang/IllegalStateException",
477 "Unable to retrieve AudioTrack pointer for start()");
Eric Laurent7070b362010-07-16 07:43:46 -0700478 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 }
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700480
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 lpTrack->start();
482}
483
484
485// ----------------------------------------------------------------------------
486static void
487android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
488{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700489 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
490 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 jniThrowException(env, "java/lang/IllegalStateException",
492 "Unable to retrieve AudioTrack pointer for stop()");
Eric Laurent7070b362010-07-16 07:43:46 -0700493 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 }
495
496 lpTrack->stop();
497}
498
499
500// ----------------------------------------------------------------------------
501static void
502android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
503{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700504 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
505 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 jniThrowException(env, "java/lang/IllegalStateException",
507 "Unable to retrieve AudioTrack pointer for pause()");
Eric Laurent7070b362010-07-16 07:43:46 -0700508 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 }
510
511 lpTrack->pause();
512}
513
514
515// ----------------------------------------------------------------------------
516static void
517android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
518{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700519 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
520 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 jniThrowException(env, "java/lang/IllegalStateException",
522 "Unable to retrieve AudioTrack pointer for flush()");
Eric Laurent7070b362010-07-16 07:43:46 -0700523 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
525
526 lpTrack->flush();
527}
528
529// ----------------------------------------------------------------------------
530static void
531android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
532{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700533 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
534 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 jniThrowException(env, "java/lang/IllegalStateException",
536 "Unable to retrieve AudioTrack pointer for setVolume()");
Eric Laurent7070b362010-07-16 07:43:46 -0700537 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 }
539
540 lpTrack->setVolume(leftVol, rightVol);
541}
542
543// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700544
Eric Laurent532bc1c2012-04-20 12:45:03 -0700545#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
Jean-Michel Trivieac84382014-02-04 14:50:40 -0800546static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700547 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
548 if (lpTrack == NULL) {
549 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700551 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700552
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 // delete the JNI data
Ashok Bhat075e9a12014-01-06 13:45:09 +0000554 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 thiz, javaAudioTrackFields.jniData);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700556 // reset the native resources in the Java object so any attempt to access
557 // them after a call to release fails.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000558 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 if (pJniStorage) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700561 Mutex::Autolock l(sLock);
562 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
Steve Block71f2cf12011-10-20 11:56:00 +0100563 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700564 while (lpCookie->busy) {
565 if (lpCookie->cond.waitRelative(sLock,
566 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
567 NO_ERROR) {
568 break;
569 }
570 }
571 sAudioTrackCallBackCookies.remove(lpCookie);
572 // delete global refs created in native_setup
573 env->DeleteGlobalRef(lpCookie->audioTrack_class);
574 env->DeleteGlobalRef(lpCookie->audioTrack_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 delete pJniStorage;
576 }
577}
578
Glenn Kasten18db49a2012-03-12 16:29:55 -0700579
Eric Laurent532bc1c2012-04-20 12:45:03 -0700580// ----------------------------------------------------------------------------
Jean-Michel Trivieac84382014-02-04 14:50:40 -0800581static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) {
582 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
583 android_media_AudioTrack_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584}
585
Andy Hung4aacc902015-04-14 15:01:29 -0700586// overloaded JNI array helper functions (same as in android_media_AudioRecord)
587static inline
588jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
589 return env->GetByteArrayElements(array, isCopy);
590}
591
592static inline
593void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
594 env->ReleaseByteArrayElements(array, elems, mode);
595}
596
597static inline
598jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
599 return env->GetShortArrayElements(array, isCopy);
600}
601
602static inline
603void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
604 env->ReleaseShortArrayElements(array, elems, mode);
605}
606
607static inline
608jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
609 return env->GetFloatArrayElements(array, isCopy);
610}
611
612static inline
613void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
614 env->ReleaseFloatArrayElements(array, elems, mode);
615}
616
Eric Laurent219de732016-05-23 12:41:50 -0700617static inline
618jint interpretWriteSizeError(ssize_t writeSize) {
619 if (writeSize == WOULD_BLOCK) {
620 return (jint)0;
621 } else if (writeSize == NO_INIT) {
622 return AUDIO_JAVA_DEAD_OBJECT;
623 } else {
624 ALOGE("Error %zd during AudioTrack native read", writeSize);
625 return nativeToJavaStatus(writeSize);
626 }
627}
628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629// ----------------------------------------------------------------------------
Andy Hung4aacc902015-04-14 15:01:29 -0700630template <typename T>
631static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
632 jint offsetInSamples, jint sizeInSamples, bool blocking) {
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700633 // give the data to the native AudioTrack object (the data starts at the offset)
634 ssize_t written = 0;
635 // regular write() or copy the data to the AudioTrack's shared memory?
Andy Hung4aacc902015-04-14 15:01:29 -0700636 size_t sizeInBytes = sizeInSamples * sizeof(T);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700637 if (track->sharedBuffer() == 0) {
Andy Hung4aacc902015-04-14 15:01:29 -0700638 written = track->write(data + offsetInSamples, sizeInBytes, blocking);
Glenn Kasten9b53db32013-07-10 14:13:39 -0700639 // for compatibility with earlier behavior of write(), return 0 in this case
640 if (written == (ssize_t) WOULD_BLOCK) {
641 written = 0;
642 }
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700643 } else {
Andy Hung2c0e17c2015-01-12 15:07:14 -0800644 // writing to shared memory, check for capacity
645 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
646 sizeInBytes = track->sharedBuffer()->size();
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700647 }
Andy Hung4aacc902015-04-14 15:01:29 -0700648 memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
Andy Hung2c0e17c2015-01-12 15:07:14 -0800649 written = sizeInBytes;
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700650 }
Eric Laurent219de732016-05-23 12:41:50 -0700651 if (written >= 0) {
Andy Hung4aacc902015-04-14 15:01:29 -0700652 return written / sizeof(T);
653 }
Eric Laurent219de732016-05-23 12:41:50 -0700654 return interpretWriteSizeError(written);
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700655}
656
657// ----------------------------------------------------------------------------
Andy Hung4aacc902015-04-14 15:01:29 -0700658template <typename T>
659static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
660 T javaAudioData,
661 jint offsetInSamples, jint sizeInSamples,
662 jint javaAudioFormat,
663 jboolean isWriteBlocking) {
664 //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
665 // offsetInSamples, sizeInSamples);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700666 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 if (lpTrack == NULL) {
668 jniThrowException(env, "java/lang/IllegalStateException",
669 "Unable to retrieve AudioTrack pointer for write()");
Andy Hung4aacc902015-04-14 15:01:29 -0700670 return (jint)AUDIO_JAVA_INVALID_OPERATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 }
672
Andy Hung4aacc902015-04-14 15:01:29 -0700673 if (javaAudioData == NULL) {
674 ALOGE("NULL java array of audio data to play");
675 return (jint)AUDIO_JAVA_BAD_VALUE;
676 }
677
Eric Laurent421ddc02011-03-07 14:52:59 -0800678 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
679 // a way that it becomes much more efficient. When doing so, we will have to prevent the
680 // AudioSystem callback to be called while in critical section (in case of media server
681 // process crash for instance)
Andy Hung4aacc902015-04-14 15:01:29 -0700682
683 // get the pointer for the audio data from the java array
684 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
685 if (cAudioData == NULL) {
686 ALOGE("Error retrieving source of audio data to play");
687 return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 }
689
Andy Hung4aacc902015-04-14 15:01:29 -0700690 jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
691 offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692
Andy Hung4aacc902015-04-14 15:01:29 -0700693 envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694
Andy Hung4aacc902015-04-14 15:01:29 -0700695 //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
696 // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
697 return samplesWritten;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698}
699
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700// ----------------------------------------------------------------------------
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800701static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
Jean-Michel Trivi5d21f672014-03-20 17:02:31 -0700702 jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800703 jint javaAudioFormat, jboolean isWriteBlocking) {
704 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
705 // offsetInBytes, sizeInBytes);
706 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
707 if (lpTrack == NULL) {
708 jniThrowException(env, "java/lang/IllegalStateException",
709 "Unable to retrieve AudioTrack pointer for write()");
Andy Hung4aacc902015-04-14 15:01:29 -0700710 return (jint)AUDIO_JAVA_INVALID_OPERATION;
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800711 }
712
713 ScopedBytesRO bytes(env, javaBytes);
714 if (bytes.get() == NULL) {
715 ALOGE("Error retrieving source of audio data to play, can't play");
Eric Laurentbc11a692014-05-16 12:19:25 -0700716 return (jint)AUDIO_JAVA_BAD_VALUE;
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800717 }
718
Jean-Michel Trivi5d21f672014-03-20 17:02:31 -0700719 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800720 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
721
722 return written;
723}
724
725// ----------------------------------------------------------------------------
Phil Burk10a33e42016-01-08 12:40:41 -0800726static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700727 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
728 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 jniThrowException(env, "java/lang/IllegalStateException",
Phil Burk10a33e42016-01-08 12:40:41 -0800730 "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()");
731 return (jint)AUDIO_JAVA_ERROR;
732 }
733
734 ssize_t result = lpTrack->getBufferSizeInFrames();
735 if (result < 0) {
Ben Wagner85ea32b2016-03-16 17:10:42 -0400736 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
737 "Internal error detected in getBufferSizeInFrames() = %zd", result);
Phil Burk10a33e42016-01-08 12:40:41 -0800738 return (jint)AUDIO_JAVA_ERROR;
739 }
740 return (jint)result;
741}
742
743// ----------------------------------------------------------------------------
744static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env,
745 jobject thiz, jint bufferSizeInFrames) {
746 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
747 if (lpTrack == NULL) {
748 jniThrowException(env, "java/lang/IllegalStateException",
749 "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()");
750 return (jint)AUDIO_JAVA_ERROR;
751 }
752 // Value will be coerced into the valid range.
753 // But internal values are unsigned, size_t, so we need to clip
754 // against zero here where it is signed.
755 if (bufferSizeInFrames < 0) {
756 bufferSizeInFrames = 0;
757 }
758 ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames);
759 if (result < 0) {
Ben Wagner85ea32b2016-03-16 17:10:42 -0400760 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
761 "Internal error detected in setBufferSizeInFrames() = %zd", result);
Phil Burk10a33e42016-01-08 12:40:41 -0800762 return (jint)AUDIO_JAVA_ERROR;
763 }
764 return (jint)result;
765}
766
767// ----------------------------------------------------------------------------
768static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env, jobject thiz) {
769 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
770 if (lpTrack == NULL) {
771 jniThrowException(env, "java/lang/IllegalStateException",
772 "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700773 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700775
776 return lpTrack->frameCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777}
778
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779// ----------------------------------------------------------------------------
Eric Laurent88e209d2009-07-07 07:10:45 -0700780static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 jint sampleRateInHz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700782 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
783 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 jniThrowException(env, "java/lang/IllegalStateException",
785 "Unable to retrieve AudioTrack pointer for setSampleRate()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700786 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700788 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789}
790
791
792// ----------------------------------------------------------------------------
793static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700794 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
795 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 jniThrowException(env, "java/lang/IllegalStateException",
797 "Unable to retrieve AudioTrack pointer for getSampleRate()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700798 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700800 return (jint) lpTrack->getSampleRate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801}
802
803
804// ----------------------------------------------------------------------------
Wei Jia2d61e2b2015-05-08 15:23:28 -0700805static void android_media_AudioTrack_set_playback_params(JNIEnv *env, jobject thiz,
806 jobject params) {
Andy Hung263b4c92015-04-16 11:16:29 -0700807 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
808 if (lpTrack == NULL) {
809 jniThrowException(env, "java/lang/IllegalStateException",
810 "AudioTrack not initialized");
811 return;
812 }
813
Andy Hung973b8852015-05-13 15:15:25 -0700814 PlaybackParams pbp;
815 pbp.fillFromJobject(env, gPlaybackParamsFields, params);
Andy Hungfe48e0d2015-04-27 18:14:02 -0700816
Wei Jia2d61e2b2015-05-08 15:23:28 -0700817 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
Andy Hung973b8852015-05-13 15:15:25 -0700818 pbp.speedSet, pbp.audioRate.mSpeed,
819 pbp.pitchSet, pbp.audioRate.mPitch,
820 pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
821 pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
Andy Hungfe48e0d2015-04-27 18:14:02 -0700822
Andy Hung973b8852015-05-13 15:15:25 -0700823 // to simulate partially set params, we do a read-modify-write.
824 // TODO: pass in the valid set mask into AudioTrack.
825 AudioPlaybackRate rate = lpTrack->getPlaybackRate();
826 bool updatedRate = false;
827 if (pbp.speedSet) {
828 rate.mSpeed = pbp.audioRate.mSpeed;
829 updatedRate = true;
830 }
831 if (pbp.pitchSet) {
832 rate.mPitch = pbp.audioRate.mPitch;
833 updatedRate = true;
834 }
835 if (pbp.audioFallbackModeSet) {
836 rate.mFallbackMode = pbp.audioRate.mFallbackMode;
837 updatedRate = true;
838 }
839 if (pbp.audioStretchModeSet) {
840 rate.mStretchMode = pbp.audioRate.mStretchMode;
841 updatedRate = true;
842 }
843 if (updatedRate) {
844 if (lpTrack->setPlaybackRate(rate) != OK) {
845 jniThrowException(env, "java/lang/IllegalArgumentException",
846 "arguments out of range");
847 }
Andy Hung263b4c92015-04-16 11:16:29 -0700848 }
849}
850
851
852// ----------------------------------------------------------------------------
Wei Jia2d61e2b2015-05-08 15:23:28 -0700853static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env, jobject thiz,
854 jobject params) {
Andy Hung263b4c92015-04-16 11:16:29 -0700855 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
856 if (lpTrack == NULL) {
857 jniThrowException(env, "java/lang/IllegalStateException",
858 "AudioTrack not initialized");
Andy Hungfe48e0d2015-04-27 18:14:02 -0700859 return NULL;
Andy Hung263b4c92015-04-16 11:16:29 -0700860 }
861
Wei Jia2d61e2b2015-05-08 15:23:28 -0700862 PlaybackParams pbs;
Andy Hungfe48e0d2015-04-27 18:14:02 -0700863 pbs.audioRate = lpTrack->getPlaybackRate();
864 pbs.speedSet = true;
865 pbs.pitchSet = true;
866 pbs.audioFallbackModeSet = true;
867 pbs.audioStretchModeSet = true;
Wei Jia2d61e2b2015-05-08 15:23:28 -0700868 return pbs.asJobject(env, gPlaybackParamsFields);
Andy Hung263b4c92015-04-16 11:16:29 -0700869}
870
871
872// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700873static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 jint markerPos) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700875 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
876 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 jniThrowException(env, "java/lang/IllegalStateException",
878 "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700879 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700881 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882}
883
884
885// ----------------------------------------------------------------------------
886static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700887 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700889
Eric Laurent532bc1c2012-04-20 12:45:03 -0700890 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 jniThrowException(env, "java/lang/IllegalStateException",
892 "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700893 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700895 lpTrack->getMarkerPosition(&markerPos);
896 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897}
898
899
900// ----------------------------------------------------------------------------
901static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz,
902 jint period) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700903 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
904 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 jniThrowException(env, "java/lang/IllegalStateException",
906 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700907 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700908 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700909 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910}
911
912
913// ----------------------------------------------------------------------------
914static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700915 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700917
Eric Laurent532bc1c2012-04-20 12:45:03 -0700918 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 jniThrowException(env, "java/lang/IllegalStateException",
920 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700921 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700923 lpTrack->getPositionUpdatePeriod(&period);
924 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925}
926
927
928// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700929static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 jint position) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700931 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
932 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 jniThrowException(env, "java/lang/IllegalStateException",
934 "Unable to retrieve AudioTrack pointer for setPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700935 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700937 return nativeToJavaStatus( lpTrack->setPosition(position) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938}
939
940
941// ----------------------------------------------------------------------------
942static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700943 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 uint32_t position = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700945
Eric Laurent532bc1c2012-04-20 12:45:03 -0700946 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 jniThrowException(env, "java/lang/IllegalStateException",
948 "Unable to retrieve AudioTrack pointer for getPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700949 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700951 lpTrack->getPosition(&position);
952 return (jint)position;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953}
954
955
956// ----------------------------------------------------------------------------
Oliver Woodman61dcdf32013-06-26 12:43:36 +0100957static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) {
958 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
959
960 if (lpTrack == NULL) {
961 jniThrowException(env, "java/lang/IllegalStateException",
962 "Unable to retrieve AudioTrack pointer for latency()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700963 return (jint)AUDIO_JAVA_ERROR;
Oliver Woodman61dcdf32013-06-26 12:43:36 +0100964 }
965 return (jint)lpTrack->latency();
966}
967
Phil Burk03f61bb2016-01-17 21:49:58 +0000968// ----------------------------------------------------------------------------
969static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject thiz) {
970 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
971
972 if (lpTrack == NULL) {
973 jniThrowException(env, "java/lang/IllegalStateException",
974 "Unable to retrieve AudioTrack pointer for getUnderrunCount()");
975 return (jint)AUDIO_JAVA_ERROR;
976 }
977 return (jint)lpTrack->getUnderrunCount();
978}
Oliver Woodman61dcdf32013-06-26 12:43:36 +0100979
980// ----------------------------------------------------------------------------
Andy Hungebc2c142017-01-12 19:20:29 -0800981static jint android_media_AudioTrack_get_flags(JNIEnv *env, jobject thiz) {
982 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
983
984 if (lpTrack == NULL) {
985 jniThrowException(env, "java/lang/IllegalStateException",
986 "Unable to retrieve AudioTrack pointer for getFlags()");
987 return (jint)AUDIO_JAVA_ERROR;
988 }
989 return (jint)lpTrack->getFlags();
990}
991
992// ----------------------------------------------------------------------------
Glenn Kasten948c2e62013-09-04 13:51:29 -0700993static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) {
994 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
995
996 if (lpTrack == NULL) {
997 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700998 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten948c2e62013-09-04 13:51:29 -0700999 }
1000 AudioTimestamp timestamp;
1001 status_t status = lpTrack->getTimestamp(timestamp);
1002 if (status == OK) {
1003 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
1004 if (nTimestamp == NULL) {
1005 ALOGE("Unable to get array for getTimestamp()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001006 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten948c2e62013-09-04 13:51:29 -07001007 }
1008 nTimestamp[0] = (jlong) timestamp.mPosition;
1009 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
1010 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
1011 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001012 return (jint) nativeToJavaStatus(status);
Glenn Kasten948c2e62013-09-04 13:51:29 -07001013}
1014
Ray Essick510225b2018-01-24 14:27:16 -08001015// ----------------------------------------------------------------------------
1016static jobject
1017android_media_AudioTrack_native_getMetrics(JNIEnv *env, jobject thiz)
1018{
1019 ALOGD("android_media_AudioTrack_native_getMetrics");
1020
1021 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1022
1023 if (lpTrack == NULL) {
1024 ALOGE("Unable to retrieve AudioTrack pointer for getMetrics()");
1025 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1026 return (jobject) NULL;
1027 }
1028
1029 // get what we have for the metrics from the track
1030 MediaAnalyticsItem *item = NULL;
1031
1032 status_t err = lpTrack->getMetrics(item);
1033 if (err != OK) {
1034 ALOGE("getMetrics failed");
1035 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1036 return (jobject) NULL;
1037 }
1038
1039 jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
1040
1041 // housekeeping
1042 delete item;
1043 item = NULL;
1044
1045 return mybundle;
1046}
1047
Glenn Kasten948c2e62013-09-04 13:51:29 -07001048
1049// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz,
1051 jint loopStart, jint loopEnd, jint loopCount) {
Eric Laurent532bc1c2012-04-20 12:45:03 -07001052 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1053 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 jniThrowException(env, "java/lang/IllegalStateException",
1055 "Unable to retrieve AudioTrack pointer for setLoop()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001056 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001058 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059}
1060
1061
1062// ----------------------------------------------------------------------------
1063static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -07001064 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1065 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 jniThrowException(env, "java/lang/IllegalStateException",
1067 "Unable to retrieve AudioTrack pointer for reload()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001068 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001070 return nativeToJavaStatus( lpTrack->reload() );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071}
1072
1073
1074// ----------------------------------------------------------------------------
1075static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz,
1076 jint javaStreamType) {
Glenn Kasten85fbc872012-11-14 13:21:09 -08001077 uint32_t afSamplingRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 // convert the stream type from Java to native value
Jean-Michel Trivieac84382014-02-04 14:50:40 -08001079 // FIXME: code duplication with android_media_AudioTrack_setup()
Dima Zavin24fc2fb2011-04-19 22:30:36 -07001080 audio_stream_type_t nativeStreamType;
Glenn Kasten29a09092012-01-16 14:37:12 -08001081 switch (javaStreamType) {
1082 case AUDIO_STREAM_VOICE_CALL:
1083 case AUDIO_STREAM_SYSTEM:
1084 case AUDIO_STREAM_RING:
1085 case AUDIO_STREAM_MUSIC:
1086 case AUDIO_STREAM_ALARM:
1087 case AUDIO_STREAM_NOTIFICATION:
1088 case AUDIO_STREAM_BLUETOOTH_SCO:
1089 case AUDIO_STREAM_DTMF:
1090 nativeStreamType = (audio_stream_type_t) javaStreamType;
1091 break;
1092 default:
Dima Zavin24fc2fb2011-04-19 22:30:36 -07001093 nativeStreamType = AUDIO_STREAM_DEFAULT;
Glenn Kasten29a09092012-01-16 14:37:12 -08001094 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 }
1096
Glenn Kasten8f81d082012-11-28 14:37:48 -08001097 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
1098 if (status != NO_ERROR) {
1099 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
1100 "in AudioTrack JNI", status, nativeStreamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 return DEFAULT_OUTPUT_SAMPLE_RATE;
1102 } else {
1103 return afSamplingRate;
1104 }
1105}
1106
1107
1108// ----------------------------------------------------------------------------
1109// returns the minimum required size for the successful creation of a streaming AudioTrack
1110// returns -1 if there was an error querying the hardware.
1111static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
Glenn Kasten5b8fd442013-11-14 09:44:14 -08001112 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +08001113
Glenn Kasten659a9712014-01-08 11:38:33 -08001114 size_t frameCount;
1115 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
1116 sampleRateInHertz);
1117 if (status != NO_ERROR) {
1118 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
1119 sampleRateInHertz, status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 return -1;
1121 }
Glenn Kastenfe834d32014-01-08 14:49:08 -08001122 const audio_format_t format = audioFormatToNative(audioFormat);
Phil Burk43f4b272016-01-27 15:35:20 -08001123 if (audio_has_proportional_frames(format)) {
Eric Laurentff0d9f02014-06-09 17:23:02 -07001124 const size_t bytesPerSample = audio_bytes_per_sample(format);
1125 return frameCount * channelCount * bytesPerSample;
1126 } else {
1127 return frameCount;
1128 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129}
1130
Eric Laurent7070b362010-07-16 07:43:46 -07001131// ----------------------------------------------------------------------------
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001132static jint
Eric Laurent7070b362010-07-16 07:43:46 -07001133android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
1134{
Eric Laurent532bc1c2012-04-20 12:45:03 -07001135 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
Eric Laurent7070b362010-07-16 07:43:46 -07001136 if (lpTrack == NULL ) {
1137 jniThrowException(env, "java/lang/IllegalStateException",
1138 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001139 return -1;
Eric Laurent7070b362010-07-16 07:43:46 -07001140 }
1141
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001142 status_t status = lpTrack->setAuxEffectSendLevel(level);
1143 if (status != NO_ERROR) {
1144 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
1145 level, status);
1146 }
1147 return (jint) status;
Eric Laurent7070b362010-07-16 07:43:46 -07001148}
1149
1150// ----------------------------------------------------------------------------
1151static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz,
1152 jint effectId) {
Eric Laurent532bc1c2012-04-20 12:45:03 -07001153 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1154 if (lpTrack == NULL) {
Eric Laurent7070b362010-07-16 07:43:46 -07001155 jniThrowException(env, "java/lang/IllegalStateException",
1156 "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001157 return (jint)AUDIO_JAVA_ERROR;
Eric Laurent7070b362010-07-16 07:43:46 -07001158 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001159 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
Eric Laurent7070b362010-07-16 07:43:46 -07001160}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161
Paul McLean88e1d862015-04-06 16:36:51 -07001162static jboolean android_media_AudioTrack_setOutputDevice(
1163 JNIEnv *env, jobject thiz, jint device_id) {
1164
1165 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
Paul McLeancef696e2015-05-21 08:51:18 -07001166 if (lpTrack == 0) {
1167 return false;
1168 }
Paul McLean88e1d862015-04-06 16:36:51 -07001169 return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1170}
1171
Eric Laurent4bcdba82015-05-01 11:37:49 -07001172static jint android_media_AudioTrack_getRoutedDeviceId(
1173 JNIEnv *env, jobject thiz) {
1174
1175 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1176 if (lpTrack == NULL) {
1177 return 0;
1178 }
1179 return (jint)lpTrack->getRoutedDeviceId();
1180}
1181
1182static void android_media_AudioTrack_enableDeviceCallback(
1183 JNIEnv *env, jobject thiz) {
1184
1185 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1186 if (lpTrack == NULL) {
1187 return;
1188 }
1189 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1190 thiz, javaAudioTrackFields.jniData);
1191 if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1192 return;
1193 }
1194 pJniStorage->mDeviceCallback =
1195 new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1196 javaAudioTrackFields.postNativeEventInJava);
1197 lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1198}
1199
1200static void android_media_AudioTrack_disableDeviceCallback(
1201 JNIEnv *env, jobject thiz) {
1202
1203 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1204 if (lpTrack == NULL) {
1205 return;
1206 }
1207 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1208 thiz, javaAudioTrackFields.jniData);
1209 if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1210 return;
1211 }
1212 lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1213 pJniStorage->mDeviceCallback.clear();
1214}
1215
Glenn Kastenbd2c3d62015-12-14 12:21:03 -08001216static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
1217 return FCC_8;
1218}
1219
Andy Hung035d4ec2017-01-24 13:45:02 -08001220// Pass through the arguments to the AudioFlinger track implementation.
1221static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
1222 jobject jconfig, jobject joperation) {
1223 // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
1224 const int VOLUME_SHAPER_INVALID_OPERATION = -38;
1225
1226 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1227 if (lpTrack == nullptr) {
1228 return (jint)VOLUME_SHAPER_INVALID_OPERATION;
1229 }
1230
1231 sp<VolumeShaper::Configuration> configuration;
1232 sp<VolumeShaper::Operation> operation;
1233 if (jconfig != nullptr) {
1234 configuration = VolumeShaperHelper::convertJobjectToConfiguration(
1235 env, gVolumeShaperFields, jconfig);
1236 ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
1237 }
1238 if (joperation != nullptr) {
1239 operation = VolumeShaperHelper::convertJobjectToOperation(
1240 env, gVolumeShaperFields, joperation);
1241 ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
1242 }
1243 VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation);
1244 if (status == INVALID_OPERATION) {
1245 status = VOLUME_SHAPER_INVALID_OPERATION;
1246 }
1247 return (jint)status; // if status < 0 an error, else a VolumeShaper id
1248}
1249
1250// Pass through the arguments to the AudioFlinger track implementation.
1251static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz,
1252 jint id) {
1253 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1254 if (lpTrack == nullptr) {
1255 return (jobject)nullptr;
1256 }
1257
1258 sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id);
1259 if (state.get() == nullptr) {
1260 return (jobject)nullptr;
1261 }
1262 return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
1263}
Eric Laurent4bcdba82015-05-01 11:37:49 -07001264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265// ----------------------------------------------------------------------------
1266// ----------------------------------------------------------------------------
Daniel Micay76f6a862015-09-19 17:31:01 -04001267static const JNINativeMethod gMethods[] = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 // name, signature, funcPtr
1269 {"native_start", "()V", (void *)android_media_AudioTrack_start},
1270 {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
1271 {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
1272 {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08001273 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
Jean-Michel Trivieac84382014-02-04 14:50:40 -08001274 (void *)android_media_AudioTrack_setup},
1275 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
1276 {"native_release", "()V", (void *)android_media_AudioTrack_release},
Andy Hung4aacc902015-04-14 15:01:29 -07001277 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -08001278 {"native_write_native_bytes",
Jean-Michel Trivi5d21f672014-03-20 17:02:31 -07001279 "(Ljava/lang/Object;IIIZ)I",
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -08001280 (void *)android_media_AudioTrack_write_native_bytes},
Andy Hung4aacc902015-04-14 15:01:29 -07001281 {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1282 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
Phil Burk10a33e42016-01-08 12:40:41 -08001284 {"native_get_buffer_size_frames",
1285 "()I", (void *)android_media_AudioTrack_get_buffer_size_frames},
1286 {"native_set_buffer_size_frames",
1287 "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames},
1288 {"native_get_buffer_capacity_frames",
1289 "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 {"native_set_playback_rate",
Eric Laurent88e209d2009-07-07 07:10:45 -07001291 "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 {"native_get_playback_rate",
1293 "()I", (void *)android_media_AudioTrack_get_playback_rate},
Wei Jia2d61e2b2015-05-08 15:23:28 -07001294 {"native_set_playback_params",
1295 "(Landroid/media/PlaybackParams;)V",
1296 (void *)android_media_AudioTrack_set_playback_params},
1297 {"native_get_playback_params",
1298 "()Landroid/media/PlaybackParams;",
1299 (void *)android_media_AudioTrack_get_playback_params},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos},
1301 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos},
1302 {"native_set_pos_update_period",
1303 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period},
1304 {"native_get_pos_update_period",
1305 "()I", (void *)android_media_AudioTrack_get_pos_update_period},
1306 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
1307 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
Oliver Woodman61dcdf32013-06-26 12:43:36 +01001308 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
Phil Burk03f61bb2016-01-17 21:49:58 +00001309 {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
Andy Hungebc2c142017-01-12 19:20:29 -08001310 {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
Glenn Kasten948c2e62013-09-04 13:51:29 -07001311 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
Ray Essick510225b2018-01-24 14:27:16 -08001312 {"native_getMetrics", "()Landroid/os/PersistableBundle;",
1313 (void *)android_media_AudioTrack_native_getMetrics},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
1315 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
1316 {"native_get_output_sample_rate",
1317 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate},
1318 {"native_get_min_buff_size",
1319 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
Eric Laurent7070b362010-07-16 07:43:46 -07001320 {"native_setAuxEffectSendLevel",
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001321 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel},
Eric Laurent7070b362010-07-16 07:43:46 -07001322 {"native_attachAuxEffect",
1323 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
Paul McLean88e1d862015-04-06 16:36:51 -07001324 {"native_setOutputDevice", "(I)Z",
1325 (void *)android_media_AudioTrack_setOutputDevice},
Eric Laurent4bcdba82015-05-01 11:37:49 -07001326 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1327 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1328 {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
Glenn Kasten25d3c7c2016-01-07 10:59:35 -08001329 {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8},
Andy Hung035d4ec2017-01-24 13:45:02 -08001330 {"native_applyVolumeShaper",
1331 "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
1332 (void *)android_media_AudioTrack_apply_volume_shaper},
1333 {"native_getVolumeShaperState",
1334 "(I)Landroid/media/VolumeShaper$State;",
1335 (void *)android_media_AudioTrack_get_volume_shaper_state},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336};
1337
1338
1339// field names found in android/media/AudioTrack.java
1340#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001341#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
1342#define JAVA_JNIDATA_FIELD_NAME "mJniData"
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001343#define JAVA_STREAMTYPE_FIELD_NAME "mStreamType"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345// ----------------------------------------------------------------------------
1346// preconditions:
1347// theClass is valid
1348bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1349 const char* constName, int* constVal) {
1350 jfieldID javaConst = NULL;
1351 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1352 if (javaConst != NULL) {
1353 *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1354 return true;
1355 } else {
Steve Block3762c312012-01-06 19:20:56 +00001356 ALOGE("Can't find %s.%s", className, constName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 return false;
1358 }
1359}
1360
1361
1362// ----------------------------------------------------------------------------
1363int register_android_media_AudioTrack(JNIEnv *env)
1364{
Glenn Kasten931fde42016-01-07 15:59:38 -08001365 // must be first
1366 int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1367
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1369 javaAudioTrackFields.postNativeEventInJava = NULL;
1370
1371 // Get the AudioTrack class
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001372 jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373
1374 // Get the postEvent method
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001375 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1376 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1377 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378
1379 // Get the variables fields
1380 // nativeTrackInJavaObj
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001381 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1382 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001383 // jniData
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001384 javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1385 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001386 // fieldStreamType
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001387 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1388 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001389
Andy Hungfe48e0d2015-04-27 18:14:02 -07001390 env->DeleteLocalRef(audioTrackClass);
1391
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001392 // Get the AudioAttributes class and fields
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001393 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
1394 javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I");
1395 javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env,
1396 audioAttrClass, "mContentType", "I");
1397 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
1398 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
1399 audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400
Andy Hungfe48e0d2015-04-27 18:14:02 -07001401 env->DeleteLocalRef(audioAttrClass);
1402
Wei Jia2d61e2b2015-05-08 15:23:28 -07001403 // initialize PlaybackParams field info
1404 gPlaybackParamsFields.init(env);
Andy Hungfe48e0d2015-04-27 18:14:02 -07001405
Andy Hung035d4ec2017-01-24 13:45:02 -08001406 gVolumeShaperFields.init(env);
Glenn Kasten931fde42016-01-07 15:59:38 -08001407 return res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408}
1409
1410
1411// ----------------------------------------------------------------------------