blob: c5049ecd37847dadb60232efafc0398aa57bbece [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>
Andreas Gampeed6b9df2014-11-20 22:02:20 -080023#include "core_jni_helpers.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024
Glenn Kastenc81d31c2012-03-13 14:46:23 -070025#include <utils/Log.h>
Jean-Michel Trivi157cba42019-01-25 18:40:03 -080026#include <media/AudioParameter.h>
Glenn Kastenc81d31c2012-03-13 14:46:23 -070027#include <media/AudioSystem.h>
28#include <media/AudioTrack.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029
Jean-Michel Trivi03f51392018-08-31 15:47:13 -070030#include <android-base/macros.h>
Mathias Agopian07952722009-05-19 19:08:10 -070031#include <binder/MemoryHeapBase.h>
32#include <binder/MemoryBase.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
Glenn Kastenfe834d32014-01-08 14:49:08 -080034#include "android_media_AudioFormat.h"
Eric Laurentbc11a692014-05-16 12:19:25 -070035#include "android_media_AudioErrors.h"
Ray Essick510225b2018-01-24 14:27:16 -080036#include "android_media_MediaMetricsJNI.h"
Wei Jia2d61e2b2015-05-08 15:23:28 -070037#include "android_media_PlaybackParams.h"
Eric Laurent4bcdba82015-05-01 11:37:49 -070038#include "android_media_DeviceCallback.h"
Andy Hung035d4ec2017-01-24 13:45:02 -080039#include "android_media_VolumeShaper.h"
François Gaffieb4691282018-07-09 13:07:32 +020040#include "android_media_AudioAttributes.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";
52
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070053struct audio_track_fields_t {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 jmethodID postNativeEventInJava; //... event post callback method
Eric Laurent83b36852009-07-28 07:49:22 -070056 jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 jfieldID jniData; // stores in Java additional resources used by the native AudioTrack
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070058 jfieldID fieldStreamType; // ... mStreamType field in the AudioTrack Java object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059};
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -070060static audio_track_fields_t javaAudioTrackFields;
Wei Jia2d61e2b2015-05-08 15:23:28 -070061static PlaybackParams::fields_t gPlaybackParamsFields;
Andy Hung035d4ec2017-01-24 13:45:02 -080062static VolumeShaperHelper::fields_t gVolumeShaperFields;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
64struct audiotrack_callback_cookie {
65 jclass audioTrack_class;
66 jobject audioTrack_ref;
Eric Laurent532bc1c2012-04-20 12:45:03 -070067 bool busy;
68 Condition cond;
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -080069 bool isOffload;
Eric Laurent532bc1c2012-04-20 12:45:03 -070070};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
Glenn Kasten3d301cb2012-01-16 14:46:54 -080072// keep these values in sync with AudioTrack.java
73#define MODE_STATIC 0
74#define MODE_STREAM 1
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076// ----------------------------------------------------------------------------
77class AudioTrackJniStorage {
78 public:
79 sp<MemoryHeapBase> mMemHeap;
80 sp<MemoryBase> mMemBase;
81 audiotrack_callback_cookie mCallbackData;
Eric Laurent4bcdba82015-05-01 11:37:49 -070082 sp<JNIDeviceCallback> mDeviceCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
84 AudioTrackJniStorage() {
Jean-Michel Trivi8a149682009-07-15 18:31:11 -070085 mCallbackData.audioTrack_class = 0;
86 mCallbackData.audioTrack_ref = 0;
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -080087 mCallbackData.isOffload = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 }
89
90 ~AudioTrackJniStorage() {
91 mMemBase.clear();
92 mMemHeap.clear();
93 }
94
95 bool allocSharedMem(int sizeInBytes) {
96 mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
97 if (mMemHeap->getHeapID() < 0) {
98 return false;
99 }
100 mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
101 return true;
102 }
103};
104
Eric Laurent532bc1c2012-04-20 12:45:03 -0700105static Mutex sLock;
106static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108// ----------------------------------------------------------------------------
109#define DEFAULT_OUTPUT_SAMPLE_RATE 44100
110
Chih-Hung Hsieh0ca16ef2016-05-19 15:14:54 -0700111#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM (-16)
112#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK (-17)
113#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT (-18)
114#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19)
115#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117// ----------------------------------------------------------------------------
Glenn Kastene46a86f2011-06-01 15:20:35 -0700118static void audioCallback(int event, void* user, void *info) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700119
120 audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
121 {
122 Mutex::Autolock l(sLock);
123 if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
124 return;
125 }
126 callbackInfo->busy = true;
127 }
128
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700129 // used as default argument when event callback doesn't have any, or number of
130 // frames for EVENT_CAN_WRITE_MORE_DATA
131 int arg = 0;
132 bool postEvent = false;
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700133 switch (event) {
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800134 // Offload only events
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700135 case AudioTrack::EVENT_CAN_WRITE_MORE_DATA:
136 // this event will read the info return parameter of the callback:
137 // for JNI offload, use the returned size to indicate:
138 // 1/ no data is returned through callback, as it's all done through write()
139 // 2/ do not wait as AudioTrack does when it receives 0 bytes
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800140 if (callbackInfo->isOffload) {
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700141 AudioTrack::Buffer* pBuffer = (AudioTrack::Buffer*) info;
142 const size_t availableForWrite = pBuffer->size;
143 arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite;
144 pBuffer->size = 0;
145 }
146 FALLTHROUGH_INTENDED;
147 case AudioTrack::EVENT_STREAM_END:
148 case AudioTrack::EVENT_NEW_IAUDIOTRACK: // a.k.a. tear down
149 if (callbackInfo->isOffload) {
150 postEvent = true;
151 }
152 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800154 // PCM and offload events
155 case AudioTrack::EVENT_MARKER:
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700156 case AudioTrack::EVENT_NEW_POS:
157 postEvent = true;
158 break;
159 default:
160 // event will not be posted
161 break;
162 }
163
164 if (postEvent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 JNIEnv *env = AndroidRuntime::getJNIEnv();
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700166 if (env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 env->CallStaticVoidMethod(
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800168 callbackInfo->audioTrack_class,
169 javaAudioTrackFields.postNativeEventInJava,
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700170 callbackInfo->audioTrack_ref, event, arg, 0, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 if (env->ExceptionCheck()) {
172 env->ExceptionDescribe();
173 env->ExceptionClear();
174 }
175 }
176 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700177
Eric Laurent532bc1c2012-04-20 12:45:03 -0700178 {
179 Mutex::Autolock l(sLock);
180 callbackInfo->busy = false;
181 callbackInfo->cond.broadcast();
182 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183}
184
185
186// ----------------------------------------------------------------------------
Eric Laurent532bc1c2012-04-20 12:45:03 -0700187static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
188{
189 Mutex::Autolock l(sLock);
190 AudioTrack* const at =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000191 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700192 return sp<AudioTrack>(at);
193}
194
195static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
196{
197 Mutex::Autolock l(sLock);
198 sp<AudioTrack> old =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000199 (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700200 if (at.get()) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800201 at->incStrong((void*)setAudioTrack);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700202 }
203 if (old != 0) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800204 old->decStrong((void*)setAudioTrack);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700205 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000206 env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700207 return old;
208}
Wei Jia071a8b72015-03-09 16:38:25 -0700209
210// ----------------------------------------------------------------------------
211sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) {
212 return getAudioTrack(env, audioTrackObj);
213}
214
Eric Laurent532bc1c2012-04-20 12:45:03 -0700215// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000216static jint
Paul McLean9b09e532016-01-26 14:43:35 -0700217android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800218 jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
Paul McLean9b09e532016-01-26 14:43:35 -0700219 jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800220 jlong nativeAudioTrack, jboolean offload) {
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700221
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700222 ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
223 " nativeAudioTrack=0x%" PRIX64 ", offload=%d",
Paul McLean9b09e532016-01-26 14:43:35 -0700224 jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700225 nativeAudioTrack, offload);
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800226
Paul McLean9b09e532016-01-26 14:43:35 -0700227 sp<AudioTrack> lpTrack = 0;
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700228
229 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000230 ALOGE("Error creating AudioTrack: invalid session ID pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700231 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent619346f2010-06-21 09:27:30 -0700232 }
233
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700234 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
235 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000236 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700237 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700238 }
Glenn Kasten33b84042016-03-08 12:02:55 -0800239 audio_session_t sessionId = (audio_session_t) nSession[0];
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700240 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
241 nSession = NULL;
242
Paul McLean9b09e532016-01-26 14:43:35 -0700243 AudioTrackJniStorage* lpJniStorage = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700244
Paul McLean9b09e532016-01-26 14:43:35 -0700245 jclass clazz = env->GetObjectClass(thiz);
246 if (clazz == NULL) {
247 ALOGE("Can't find %s when setting up callback.", kClassPathName);
248 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
249 }
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700250
Paul McLean9b09e532016-01-26 14:43:35 -0700251 // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
252 if (nativeAudioTrack == 0) {
253 if (jaa == 0) {
254 ALOGE("Error creating AudioTrack: invalid audio attributes");
255 return (jint) AUDIO_JAVA_ERROR;
256 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700257
Paul McLean9b09e532016-01-26 14:43:35 -0700258 if (jSampleRate == 0) {
259 ALOGE("Error creating AudioTrack: invalid sample rates");
260 return (jint) AUDIO_JAVA_ERROR;
261 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262
Paul McLean9b09e532016-01-26 14:43:35 -0700263 int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
264 int sampleRateInHertz = sampleRates[0];
265 env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700266
Paul McLean9b09e532016-01-26 14:43:35 -0700267 // Invalid channel representations are caught by !audio_is_output_channel() below.
268 audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
269 channelPositionMask, channelIndexMask);
270 if (!audio_is_output_channel(nativeChannelMask)) {
271 ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
272 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
273 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700274
Paul McLean9b09e532016-01-26 14:43:35 -0700275 uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
276
277 // check the format.
278 // This function was called from Java, so we compare the format against the Java constants
279 audio_format_t format = audioFormatToNative(audioFormat);
280 if (format == AUDIO_FORMAT_INVALID) {
281 ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
282 return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
283 }
284
285 // compute the frame count
286 size_t frameCount;
jiabinf2d8e9c2018-02-23 17:44:56 -0800287 if (audio_has_proportional_frames(format)) {
Paul McLean9b09e532016-01-26 14:43:35 -0700288 const size_t bytesPerSample = audio_bytes_per_sample(format);
289 frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
290 } else {
291 frameCount = buffSizeInBytes;
292 }
293
294 // create the native AudioTrack object
295 lpTrack = new AudioTrack();
296
297 // read the AudioAttributes values
François Gaffieb4691282018-07-09 13:07:32 +0200298 auto paa = JNIAudioAttributeHelper::makeUnique();
299 jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
300 if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
301 return jStatus;
302 }
Paul McLean9b09e532016-01-26 14:43:35 -0700303 ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
304 paa->usage, paa->content_type, paa->flags, paa->tags);
305
306 // initialize the callback information:
307 // this data will be passed with every AudioTrack callback
308 lpJniStorage = new AudioTrackJniStorage();
309 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
310 // we use a weak reference so the AudioTrack object can be garbage collected.
311 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800312 lpJniStorage->mCallbackData.isOffload = offload;
Paul McLean9b09e532016-01-26 14:43:35 -0700313 lpJniStorage->mCallbackData.busy = false;
314
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800315 audio_offload_info_t offloadInfo;
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700316 if (offload == JNI_TRUE) {
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800317 offloadInfo = AUDIO_INFO_INITIALIZER;
318 offloadInfo.format = format;
319 offloadInfo.sample_rate = sampleRateInHertz;
320 offloadInfo.channel_mask = nativeChannelMask;
321 offloadInfo.has_video = false;
322 offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
323 }
324
Paul McLean9b09e532016-01-26 14:43:35 -0700325 // initialize the native AudioTrack object
326 status_t status = NO_ERROR;
327 switch (memoryMode) {
328 case MODE_STREAM:
Paul McLean9b09e532016-01-26 14:43:35 -0700329 status = lpTrack->set(
330 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
331 sampleRateInHertz,
332 format,// word length, PCM
333 nativeChannelMask,
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700334 offload ? 0 : frameCount,
335 offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,
Paul McLean9b09e532016-01-26 14:43:35 -0700336 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
337 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
338 0,// shared mem
339 true,// thread can call Java
340 sessionId,// audio session ID
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700341 offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -0800342 offload ? &offloadInfo : NULL,
Paul McLean9b09e532016-01-26 14:43:35 -0700343 -1, -1, // default uid, pid values
François Gaffieb4691282018-07-09 13:07:32 +0200344 paa.get());
Jean-Michel Trivi03f51392018-08-31 15:47:13 -0700345
Paul McLean9b09e532016-01-26 14:43:35 -0700346 break;
347
348 case MODE_STATIC:
349 // AudioTrack is using shared memory
350
351 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
352 ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
353 goto native_init_failure;
354 }
355
356 status = lpTrack->set(
357 AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
358 sampleRateInHertz,
359 format,// word length, PCM
360 nativeChannelMask,
361 frameCount,
362 AUDIO_OUTPUT_FLAG_NONE,
363 audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
364 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
365 lpJniStorage->mMemBase,// shared mem
366 true,// thread can call Java
367 sessionId,// audio session ID
368 AudioTrack::TRANSFER_SHARED,
369 NULL, // default offloadInfo
370 -1, -1, // default uid, pid values
François Gaffieb4691282018-07-09 13:07:32 +0200371 paa.get());
Paul McLean9b09e532016-01-26 14:43:35 -0700372 break;
373
374 default:
375 ALOGE("Unknown mode %d", memoryMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 goto native_init_failure;
377 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700378
Paul McLean9b09e532016-01-26 14:43:35 -0700379 if (status != NO_ERROR) {
380 ALOGE("Error %d initializing AudioTrack", status);
381 goto native_init_failure;
382 }
383 } else { // end if (nativeAudioTrack == 0)
384 lpTrack = (AudioTrack*)nativeAudioTrack;
385 // TODO: We need to find out which members of the Java AudioTrack might
386 // need to be initialized from the Native AudioTrack
387 // these are directly returned from getters:
388 // mSampleRate
389 // mAudioFormat
390 // mStreamType
391 // mChannelConfiguration
392 // mChannelCount
393 // mState (?)
394 // mPlayState (?)
395 // these may be used internally (Java AudioTrack.audioParamCheck():
396 // mChannelMask
397 // mChannelIndexMask
398 // mDataLoadMode
Glenn Kasten3d301cb2012-01-16 14:46:54 -0800399
Paul McLean9b09e532016-01-26 14:43:35 -0700400 // initialize the callback information:
401 // this data will be passed with every AudioTrack callback
402 lpJniStorage = new AudioTrackJniStorage();
403 lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
404 // we use a weak reference so the AudioTrack object can be garbage collected.
405 lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
406 lpJniStorage->mCallbackData.busy = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 }
408
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700409 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
410 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000411 ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700412 goto native_init_failure;
413 }
Eric Laurent619346f2010-06-21 09:27:30 -0700414 // read the audio session ID back from AudioTrack in case we create a new session
415 nSession[0] = lpTrack->getSessionId();
Eric Laurent619346f2010-06-21 09:27:30 -0700416 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
417 nSession = NULL;
418
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800419 {
420 const jint elements[1] = { (jint) lpTrack->getSampleRate() };
421 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
422 }
423
Eric Laurent532bc1c2012-04-20 12:45:03 -0700424 { // scope for the lock
425 Mutex::Autolock l(sLock);
426 sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
427 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700428 // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 // of the Java object (in mNativeTrackInJavaObj)
Eric Laurent532bc1c2012-04-20 12:45:03 -0700430 setAudioTrack(env, thiz, lpTrack);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 // save the JNI resources so we can free them later
Ashok Bhat075e9a12014-01-06 13:45:09 +0000433 //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
434 env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700436 // since we had audio attributes, the stream type was derived from them during the
437 // creation of the native AudioTrack: push the same value to the Java object
438 env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -0700439
Eric Laurentbc11a692014-05-16 12:19:25 -0700440 return (jint) AUDIO_JAVA_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 // failures:
443native_init_failure:
Eric Laurent619346f2010-06-21 09:27:30 -0700444 if (nSession != NULL) {
445 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
446 }
Jean-Michel Trivi8a149682009-07-15 18:31:11 -0700447 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
448 env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 delete lpJniStorage;
Ashok Bhat075e9a12014-01-06 13:45:09 +0000450 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700451
Glenn Kasten14d226a2015-05-18 13:53:39 -0700452 // lpTrack goes out of scope, so reference count drops to zero
Ashok Bhat075e9a12014-01-06 13:45:09 +0000453 return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454}
455
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456// ----------------------------------------------------------------------------
Michael Chan2de156d2018-04-24 14:33:57 +1000457static jboolean
458android_media_AudioTrack_is_direct_output_supported(JNIEnv *env, jobject thiz,
459 jint encoding, jint sampleRate,
460 jint channelMask, jint channelIndexMask,
461 jint contentType, jint usage, jint flags) {
462 audio_config_base_t config = {};
463 audio_attributes_t attributes = {};
464 config.format = static_cast<audio_format_t>(audioFormatToNative(encoding));
465 config.sample_rate = static_cast<uint32_t>(sampleRate);
466 config.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
467 attributes.content_type = static_cast<audio_content_type_t>(contentType);
468 attributes.usage = static_cast<audio_usage_t>(usage);
469 attributes.flags = static_cast<audio_flags_mask_t>(flags);
470 // ignore source and tags attributes as they don't affect querying whether output is supported
471 return AudioTrack::isDirectOutputSupported(config, attributes);
472}
473
474// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475static void
476android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
477{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700478 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
479 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 jniThrowException(env, "java/lang/IllegalStateException",
481 "Unable to retrieve AudioTrack pointer for start()");
Eric Laurent7070b362010-07-16 07:43:46 -0700482 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 }
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 lpTrack->start();
486}
487
488
489// ----------------------------------------------------------------------------
490static void
491android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
492{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700493 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
494 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 jniThrowException(env, "java/lang/IllegalStateException",
496 "Unable to retrieve AudioTrack pointer for stop()");
Eric Laurent7070b362010-07-16 07:43:46 -0700497 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 }
499
500 lpTrack->stop();
501}
502
503
504// ----------------------------------------------------------------------------
505static void
506android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
507{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700508 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
509 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 jniThrowException(env, "java/lang/IllegalStateException",
511 "Unable to retrieve AudioTrack pointer for pause()");
Eric Laurent7070b362010-07-16 07:43:46 -0700512 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 }
514
515 lpTrack->pause();
516}
517
518
519// ----------------------------------------------------------------------------
520static void
521android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
522{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700523 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
524 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 jniThrowException(env, "java/lang/IllegalStateException",
526 "Unable to retrieve AudioTrack pointer for flush()");
Eric Laurent7070b362010-07-16 07:43:46 -0700527 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 }
529
530 lpTrack->flush();
531}
532
533// ----------------------------------------------------------------------------
534static void
535android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
536{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700537 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
538 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 jniThrowException(env, "java/lang/IllegalStateException",
540 "Unable to retrieve AudioTrack pointer for setVolume()");
Eric Laurent7070b362010-07-16 07:43:46 -0700541 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 }
543
544 lpTrack->setVolume(leftVol, rightVol);
545}
546
547// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700548
Eric Laurent532bc1c2012-04-20 12:45:03 -0700549#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
Jean-Michel Trivieac84382014-02-04 14:50:40 -0800550static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700551 sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
552 if (lpTrack == NULL) {
553 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700555 //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 // delete the JNI data
Ashok Bhat075e9a12014-01-06 13:45:09 +0000558 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 thiz, javaAudioTrackFields.jniData);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700560 // reset the native resources in the Java object so any attempt to access
561 // them after a call to release fails.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000562 env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 if (pJniStorage) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700565 Mutex::Autolock l(sLock);
566 audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
Steve Block71f2cf12011-10-20 11:56:00 +0100567 //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700568 while (lpCookie->busy) {
569 if (lpCookie->cond.waitRelative(sLock,
570 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
571 NO_ERROR) {
572 break;
573 }
574 }
575 sAudioTrackCallBackCookies.remove(lpCookie);
576 // delete global refs created in native_setup
577 env->DeleteGlobalRef(lpCookie->audioTrack_class);
578 env->DeleteGlobalRef(lpCookie->audioTrack_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 delete pJniStorage;
580 }
581}
582
Glenn Kasten18db49a2012-03-12 16:29:55 -0700583
Eric Laurent532bc1c2012-04-20 12:45:03 -0700584// ----------------------------------------------------------------------------
Jean-Michel Trivieac84382014-02-04 14:50:40 -0800585static void android_media_AudioTrack_finalize(JNIEnv *env, jobject thiz) {
586 //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
587 android_media_AudioTrack_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588}
589
Andy Hung4aacc902015-04-14 15:01:29 -0700590// overloaded JNI array helper functions (same as in android_media_AudioRecord)
591static inline
592jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
593 return env->GetByteArrayElements(array, isCopy);
594}
595
596static inline
597void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
598 env->ReleaseByteArrayElements(array, elems, mode);
599}
600
601static inline
602jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
603 return env->GetShortArrayElements(array, isCopy);
604}
605
606static inline
607void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
608 env->ReleaseShortArrayElements(array, elems, mode);
609}
610
611static inline
612jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
613 return env->GetFloatArrayElements(array, isCopy);
614}
615
616static inline
617void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
618 env->ReleaseFloatArrayElements(array, elems, mode);
619}
620
Eric Laurent219de732016-05-23 12:41:50 -0700621static inline
622jint interpretWriteSizeError(ssize_t writeSize) {
623 if (writeSize == WOULD_BLOCK) {
624 return (jint)0;
625 } else if (writeSize == NO_INIT) {
626 return AUDIO_JAVA_DEAD_OBJECT;
627 } else {
628 ALOGE("Error %zd during AudioTrack native read", writeSize);
629 return nativeToJavaStatus(writeSize);
630 }
631}
632
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633// ----------------------------------------------------------------------------
Andy Hung4aacc902015-04-14 15:01:29 -0700634template <typename T>
635static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
636 jint offsetInSamples, jint sizeInSamples, bool blocking) {
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700637 // give the data to the native AudioTrack object (the data starts at the offset)
638 ssize_t written = 0;
639 // regular write() or copy the data to the AudioTrack's shared memory?
Andy Hung4aacc902015-04-14 15:01:29 -0700640 size_t sizeInBytes = sizeInSamples * sizeof(T);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700641 if (track->sharedBuffer() == 0) {
Andy Hung4aacc902015-04-14 15:01:29 -0700642 written = track->write(data + offsetInSamples, sizeInBytes, blocking);
Glenn Kasten9b53db32013-07-10 14:13:39 -0700643 // for compatibility with earlier behavior of write(), return 0 in this case
644 if (written == (ssize_t) WOULD_BLOCK) {
645 written = 0;
646 }
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700647 } else {
Andy Hung2c0e17c2015-01-12 15:07:14 -0800648 // writing to shared memory, check for capacity
649 if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
650 sizeInBytes = track->sharedBuffer()->size();
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700651 }
Ytai Ben-Tsvi2c9d8f62019-09-05 15:14:31 -0700652 memcpy(track->sharedBuffer()->unsecurePointer(), data + offsetInSamples, sizeInBytes);
Andy Hung2c0e17c2015-01-12 15:07:14 -0800653 written = sizeInBytes;
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700654 }
Eric Laurent219de732016-05-23 12:41:50 -0700655 if (written >= 0) {
Andy Hung4aacc902015-04-14 15:01:29 -0700656 return written / sizeof(T);
657 }
Eric Laurent219de732016-05-23 12:41:50 -0700658 return interpretWriteSizeError(written);
Jean-Michel Trivi21dc0372009-05-08 16:06:34 -0700659}
660
661// ----------------------------------------------------------------------------
Andy Hung4aacc902015-04-14 15:01:29 -0700662template <typename T>
663static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
664 T javaAudioData,
665 jint offsetInSamples, jint sizeInSamples,
666 jint javaAudioFormat,
667 jboolean isWriteBlocking) {
668 //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
669 // offsetInSamples, sizeInSamples);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700670 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 if (lpTrack == NULL) {
672 jniThrowException(env, "java/lang/IllegalStateException",
673 "Unable to retrieve AudioTrack pointer for write()");
Andy Hung4aacc902015-04-14 15:01:29 -0700674 return (jint)AUDIO_JAVA_INVALID_OPERATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 }
676
Andy Hung4aacc902015-04-14 15:01:29 -0700677 if (javaAudioData == NULL) {
678 ALOGE("NULL java array of audio data to play");
679 return (jint)AUDIO_JAVA_BAD_VALUE;
680 }
681
Eric Laurent421ddc02011-03-07 14:52:59 -0800682 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
683 // a way that it becomes much more efficient. When doing so, we will have to prevent the
684 // AudioSystem callback to be called while in critical section (in case of media server
685 // process crash for instance)
Andy Hung4aacc902015-04-14 15:01:29 -0700686
687 // get the pointer for the audio data from the java array
688 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
689 if (cAudioData == NULL) {
690 ALOGE("Error retrieving source of audio data to play");
691 return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 }
693
Andy Hung4aacc902015-04-14 15:01:29 -0700694 jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
695 offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696
Andy Hung4aacc902015-04-14 15:01:29 -0700697 envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698
Andy Hung4aacc902015-04-14 15:01:29 -0700699 //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
700 // (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
701 return samplesWritten;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702}
703
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704// ----------------------------------------------------------------------------
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800705static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
Orion Hodson9b909c32018-11-23 11:05:27 +0000706 jobject javaByteBuffer, jint byteOffset, jint sizeInBytes,
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800707 jint javaAudioFormat, jboolean isWriteBlocking) {
708 //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
709 // offsetInBytes, sizeInBytes);
710 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
711 if (lpTrack == NULL) {
712 jniThrowException(env, "java/lang/IllegalStateException",
713 "Unable to retrieve AudioTrack pointer for write()");
Andy Hung4aacc902015-04-14 15:01:29 -0700714 return (jint)AUDIO_JAVA_INVALID_OPERATION;
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800715 }
716
Orion Hodson9b909c32018-11-23 11:05:27 +0000717 const jbyte* bytes =
718 reinterpret_cast<const jbyte*>(env->GetDirectBufferAddress(javaByteBuffer));
719 if (bytes == NULL) {
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800720 ALOGE("Error retrieving source of audio data to play, can't play");
Eric Laurentbc11a692014-05-16 12:19:25 -0700721 return (jint)AUDIO_JAVA_BAD_VALUE;
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800722 }
723
Orion Hodson9b909c32018-11-23 11:05:27 +0000724 jint written = writeToTrack(lpTrack, javaAudioFormat, bytes, byteOffset,
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -0800725 sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
726
727 return written;
728}
729
730// ----------------------------------------------------------------------------
Phil Burk10a33e42016-01-08 12:40:41 -0800731static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700732 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
733 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 jniThrowException(env, "java/lang/IllegalStateException",
Phil Burk10a33e42016-01-08 12:40:41 -0800735 "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()");
736 return (jint)AUDIO_JAVA_ERROR;
737 }
738
739 ssize_t result = lpTrack->getBufferSizeInFrames();
740 if (result < 0) {
Ben Wagner85ea32b2016-03-16 17:10:42 -0400741 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
742 "Internal error detected in getBufferSizeInFrames() = %zd", result);
Phil Burk10a33e42016-01-08 12:40:41 -0800743 return (jint)AUDIO_JAVA_ERROR;
744 }
745 return (jint)result;
746}
747
748// ----------------------------------------------------------------------------
749static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env,
750 jobject thiz, jint bufferSizeInFrames) {
751 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
752 if (lpTrack == NULL) {
753 jniThrowException(env, "java/lang/IllegalStateException",
754 "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()");
755 return (jint)AUDIO_JAVA_ERROR;
756 }
757 // Value will be coerced into the valid range.
758 // But internal values are unsigned, size_t, so we need to clip
759 // against zero here where it is signed.
760 if (bufferSizeInFrames < 0) {
761 bufferSizeInFrames = 0;
762 }
763 ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames);
764 if (result < 0) {
Ben Wagner85ea32b2016-03-16 17:10:42 -0400765 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
766 "Internal error detected in setBufferSizeInFrames() = %zd", result);
Phil Burk10a33e42016-01-08 12:40:41 -0800767 return (jint)AUDIO_JAVA_ERROR;
768 }
769 return (jint)result;
770}
771
772// ----------------------------------------------------------------------------
773static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env, jobject thiz) {
774 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
775 if (lpTrack == NULL) {
776 jniThrowException(env, "java/lang/IllegalStateException",
777 "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700778 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700780
781 return lpTrack->frameCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782}
783
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784// ----------------------------------------------------------------------------
Eric Laurent88e209d2009-07-07 07:10:45 -0700785static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 jint sampleRateInHz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700787 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
788 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 jniThrowException(env, "java/lang/IllegalStateException",
790 "Unable to retrieve AudioTrack pointer for setSampleRate()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700791 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700793 return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794}
795
796
797// ----------------------------------------------------------------------------
798static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700799 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
800 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 jniThrowException(env, "java/lang/IllegalStateException",
802 "Unable to retrieve AudioTrack pointer for getSampleRate()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700803 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700805 return (jint) lpTrack->getSampleRate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806}
807
808
809// ----------------------------------------------------------------------------
Wei Jia2d61e2b2015-05-08 15:23:28 -0700810static void android_media_AudioTrack_set_playback_params(JNIEnv *env, jobject thiz,
811 jobject params) {
Andy Hung263b4c92015-04-16 11:16:29 -0700812 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
813 if (lpTrack == NULL) {
814 jniThrowException(env, "java/lang/IllegalStateException",
815 "AudioTrack not initialized");
816 return;
817 }
818
Andy Hung973b8852015-05-13 15:15:25 -0700819 PlaybackParams pbp;
820 pbp.fillFromJobject(env, gPlaybackParamsFields, params);
Andy Hungfe48e0d2015-04-27 18:14:02 -0700821
Wei Jia2d61e2b2015-05-08 15:23:28 -0700822 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
Andy Hung973b8852015-05-13 15:15:25 -0700823 pbp.speedSet, pbp.audioRate.mSpeed,
824 pbp.pitchSet, pbp.audioRate.mPitch,
825 pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
826 pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
Andy Hungfe48e0d2015-04-27 18:14:02 -0700827
Andy Hung973b8852015-05-13 15:15:25 -0700828 // to simulate partially set params, we do a read-modify-write.
829 // TODO: pass in the valid set mask into AudioTrack.
830 AudioPlaybackRate rate = lpTrack->getPlaybackRate();
831 bool updatedRate = false;
832 if (pbp.speedSet) {
833 rate.mSpeed = pbp.audioRate.mSpeed;
834 updatedRate = true;
835 }
836 if (pbp.pitchSet) {
837 rate.mPitch = pbp.audioRate.mPitch;
838 updatedRate = true;
839 }
840 if (pbp.audioFallbackModeSet) {
841 rate.mFallbackMode = pbp.audioRate.mFallbackMode;
842 updatedRate = true;
843 }
844 if (pbp.audioStretchModeSet) {
845 rate.mStretchMode = pbp.audioRate.mStretchMode;
846 updatedRate = true;
847 }
848 if (updatedRate) {
849 if (lpTrack->setPlaybackRate(rate) != OK) {
850 jniThrowException(env, "java/lang/IllegalArgumentException",
851 "arguments out of range");
852 }
Andy Hung263b4c92015-04-16 11:16:29 -0700853 }
854}
855
856
857// ----------------------------------------------------------------------------
Wei Jia2d61e2b2015-05-08 15:23:28 -0700858static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env, jobject thiz,
859 jobject params) {
Andy Hung263b4c92015-04-16 11:16:29 -0700860 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
861 if (lpTrack == NULL) {
862 jniThrowException(env, "java/lang/IllegalStateException",
863 "AudioTrack not initialized");
Andy Hungfe48e0d2015-04-27 18:14:02 -0700864 return NULL;
Andy Hung263b4c92015-04-16 11:16:29 -0700865 }
866
Wei Jia2d61e2b2015-05-08 15:23:28 -0700867 PlaybackParams pbs;
Andy Hungfe48e0d2015-04-27 18:14:02 -0700868 pbs.audioRate = lpTrack->getPlaybackRate();
869 pbs.speedSet = true;
870 pbs.pitchSet = true;
871 pbs.audioFallbackModeSet = true;
872 pbs.audioStretchModeSet = true;
Wei Jia2d61e2b2015-05-08 15:23:28 -0700873 return pbs.asJobject(env, gPlaybackParamsFields);
Andy Hung263b4c92015-04-16 11:16:29 -0700874}
875
876
877// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700878static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 jint markerPos) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700880 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
881 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 jniThrowException(env, "java/lang/IllegalStateException",
883 "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700884 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700886 return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887}
888
889
890// ----------------------------------------------------------------------------
891static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700892 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700894
Eric Laurent532bc1c2012-04-20 12:45:03 -0700895 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 jniThrowException(env, "java/lang/IllegalStateException",
897 "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700898 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700900 lpTrack->getMarkerPosition(&markerPos);
901 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902}
903
904
905// ----------------------------------------------------------------------------
906static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz,
907 jint period) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700908 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
909 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 jniThrowException(env, "java/lang/IllegalStateException",
911 "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700912 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700913 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700914 return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915}
916
917
918// ----------------------------------------------------------------------------
919static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700920 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700922
Eric Laurent532bc1c2012-04-20 12:45:03 -0700923 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 jniThrowException(env, "java/lang/IllegalStateException",
925 "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700926 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700928 lpTrack->getPositionUpdatePeriod(&period);
929 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930}
931
932
933// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700934static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 jint position) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700936 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
937 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 jniThrowException(env, "java/lang/IllegalStateException",
939 "Unable to retrieve AudioTrack pointer for setPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700940 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700942 return nativeToJavaStatus( lpTrack->setPosition(position) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943}
944
945
946// ----------------------------------------------------------------------------
947static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700948 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 uint32_t position = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700950
Eric Laurent532bc1c2012-04-20 12:45:03 -0700951 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 jniThrowException(env, "java/lang/IllegalStateException",
953 "Unable to retrieve AudioTrack pointer for getPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700954 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700956 lpTrack->getPosition(&position);
957 return (jint)position;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958}
959
960
961// ----------------------------------------------------------------------------
Oliver Woodman61dcdf32013-06-26 12:43:36 +0100962static jint android_media_AudioTrack_get_latency(JNIEnv *env, jobject thiz) {
963 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
964
965 if (lpTrack == NULL) {
966 jniThrowException(env, "java/lang/IllegalStateException",
967 "Unable to retrieve AudioTrack pointer for latency()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700968 return (jint)AUDIO_JAVA_ERROR;
Oliver Woodman61dcdf32013-06-26 12:43:36 +0100969 }
970 return (jint)lpTrack->latency();
971}
972
Phil Burk03f61bb2016-01-17 21:49:58 +0000973// ----------------------------------------------------------------------------
974static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env, jobject thiz) {
975 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
976
977 if (lpTrack == NULL) {
978 jniThrowException(env, "java/lang/IllegalStateException",
979 "Unable to retrieve AudioTrack pointer for getUnderrunCount()");
980 return (jint)AUDIO_JAVA_ERROR;
981 }
982 return (jint)lpTrack->getUnderrunCount();
983}
Oliver Woodman61dcdf32013-06-26 12:43:36 +0100984
985// ----------------------------------------------------------------------------
Andy Hungebc2c142017-01-12 19:20:29 -0800986static jint android_media_AudioTrack_get_flags(JNIEnv *env, jobject thiz) {
987 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
988
989 if (lpTrack == NULL) {
990 jniThrowException(env, "java/lang/IllegalStateException",
991 "Unable to retrieve AudioTrack pointer for getFlags()");
992 return (jint)AUDIO_JAVA_ERROR;
993 }
994 return (jint)lpTrack->getFlags();
995}
996
997// ----------------------------------------------------------------------------
Glenn Kasten948c2e62013-09-04 13:51:29 -0700998static jint android_media_AudioTrack_get_timestamp(JNIEnv *env, jobject thiz, jlongArray jTimestamp) {
999 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1000
1001 if (lpTrack == NULL) {
1002 ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001003 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten948c2e62013-09-04 13:51:29 -07001004 }
1005 AudioTimestamp timestamp;
1006 status_t status = lpTrack->getTimestamp(timestamp);
1007 if (status == OK) {
1008 jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
1009 if (nTimestamp == NULL) {
1010 ALOGE("Unable to get array for getTimestamp()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001011 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten948c2e62013-09-04 13:51:29 -07001012 }
1013 nTimestamp[0] = (jlong) timestamp.mPosition;
1014 nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
1015 env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
1016 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001017 return (jint) nativeToJavaStatus(status);
Glenn Kasten948c2e62013-09-04 13:51:29 -07001018}
1019
Ray Essick510225b2018-01-24 14:27:16 -08001020// ----------------------------------------------------------------------------
1021static jobject
1022android_media_AudioTrack_native_getMetrics(JNIEnv *env, jobject thiz)
1023{
1024 ALOGD("android_media_AudioTrack_native_getMetrics");
1025
1026 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1027
1028 if (lpTrack == NULL) {
1029 ALOGE("Unable to retrieve AudioTrack pointer for getMetrics()");
1030 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1031 return (jobject) NULL;
1032 }
1033
1034 // get what we have for the metrics from the track
1035 MediaAnalyticsItem *item = NULL;
1036
1037 status_t err = lpTrack->getMetrics(item);
1038 if (err != OK) {
1039 ALOGE("getMetrics failed");
1040 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1041 return (jobject) NULL;
1042 }
1043
1044 jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
1045
1046 // housekeeping
1047 delete item;
1048 item = NULL;
1049
1050 return mybundle;
1051}
1052
Glenn Kasten948c2e62013-09-04 13:51:29 -07001053
1054// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz,
1056 jint loopStart, jint loopEnd, jint loopCount) {
Eric Laurent532bc1c2012-04-20 12:45:03 -07001057 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1058 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 jniThrowException(env, "java/lang/IllegalStateException",
1060 "Unable to retrieve AudioTrack pointer for setLoop()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001061 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001063 return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064}
1065
1066
1067// ----------------------------------------------------------------------------
1068static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) {
Eric Laurent532bc1c2012-04-20 12:45:03 -07001069 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1070 if (lpTrack == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 jniThrowException(env, "java/lang/IllegalStateException",
1072 "Unable to retrieve AudioTrack pointer for reload()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001073 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001075 return nativeToJavaStatus( lpTrack->reload() );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076}
1077
1078
1079// ----------------------------------------------------------------------------
1080static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobject thiz,
1081 jint javaStreamType) {
Glenn Kasten85fbc872012-11-14 13:21:09 -08001082 uint32_t afSamplingRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 // convert the stream type from Java to native value
Jean-Michel Trivieac84382014-02-04 14:50:40 -08001084 // FIXME: code duplication with android_media_AudioTrack_setup()
Dima Zavin24fc2fb2011-04-19 22:30:36 -07001085 audio_stream_type_t nativeStreamType;
Glenn Kasten29a09092012-01-16 14:37:12 -08001086 switch (javaStreamType) {
1087 case AUDIO_STREAM_VOICE_CALL:
1088 case AUDIO_STREAM_SYSTEM:
1089 case AUDIO_STREAM_RING:
1090 case AUDIO_STREAM_MUSIC:
1091 case AUDIO_STREAM_ALARM:
1092 case AUDIO_STREAM_NOTIFICATION:
1093 case AUDIO_STREAM_BLUETOOTH_SCO:
1094 case AUDIO_STREAM_DTMF:
1095 nativeStreamType = (audio_stream_type_t) javaStreamType;
1096 break;
1097 default:
Dima Zavin24fc2fb2011-04-19 22:30:36 -07001098 nativeStreamType = AUDIO_STREAM_DEFAULT;
Glenn Kasten29a09092012-01-16 14:37:12 -08001099 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
1101
Glenn Kasten8f81d082012-11-28 14:37:48 -08001102 status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
1103 if (status != NO_ERROR) {
1104 ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
1105 "in AudioTrack JNI", status, nativeStreamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 return DEFAULT_OUTPUT_SAMPLE_RATE;
1107 } else {
1108 return afSamplingRate;
1109 }
1110}
1111
1112
1113// ----------------------------------------------------------------------------
1114// returns the minimum required size for the successful creation of a streaming AudioTrack
1115// returns -1 if there was an error querying the hardware.
1116static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
Glenn Kasten5b8fd442013-11-14 09:44:14 -08001117 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +08001118
Glenn Kasten659a9712014-01-08 11:38:33 -08001119 size_t frameCount;
1120 const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
1121 sampleRateInHertz);
1122 if (status != NO_ERROR) {
1123 ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
1124 sampleRateInHertz, status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 return -1;
1126 }
Glenn Kastenfe834d32014-01-08 14:49:08 -08001127 const audio_format_t format = audioFormatToNative(audioFormat);
Phil Burk43f4b272016-01-27 15:35:20 -08001128 if (audio_has_proportional_frames(format)) {
Eric Laurentff0d9f02014-06-09 17:23:02 -07001129 const size_t bytesPerSample = audio_bytes_per_sample(format);
1130 return frameCount * channelCount * bytesPerSample;
1131 } else {
1132 return frameCount;
1133 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134}
1135
Eric Laurent7070b362010-07-16 07:43:46 -07001136// ----------------------------------------------------------------------------
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001137static jint
Eric Laurent7070b362010-07-16 07:43:46 -07001138android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
1139{
Eric Laurent532bc1c2012-04-20 12:45:03 -07001140 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
Eric Laurent7070b362010-07-16 07:43:46 -07001141 if (lpTrack == NULL ) {
1142 jniThrowException(env, "java/lang/IllegalStateException",
1143 "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001144 return -1;
Eric Laurent7070b362010-07-16 07:43:46 -07001145 }
1146
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001147 status_t status = lpTrack->setAuxEffectSendLevel(level);
1148 if (status != NO_ERROR) {
1149 ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
1150 level, status);
1151 }
1152 return (jint) status;
Eric Laurent7070b362010-07-16 07:43:46 -07001153}
1154
1155// ----------------------------------------------------------------------------
1156static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz,
1157 jint effectId) {
Eric Laurent532bc1c2012-04-20 12:45:03 -07001158 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1159 if (lpTrack == NULL) {
Eric Laurent7070b362010-07-16 07:43:46 -07001160 jniThrowException(env, "java/lang/IllegalStateException",
1161 "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
Eric Laurentbc11a692014-05-16 12:19:25 -07001162 return (jint)AUDIO_JAVA_ERROR;
Eric Laurent7070b362010-07-16 07:43:46 -07001163 }
Eric Laurentbc11a692014-05-16 12:19:25 -07001164 return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
Eric Laurent7070b362010-07-16 07:43:46 -07001165}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166
Paul McLean88e1d862015-04-06 16:36:51 -07001167static jboolean android_media_AudioTrack_setOutputDevice(
1168 JNIEnv *env, jobject thiz, jint device_id) {
1169
1170 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
Paul McLeancef696e2015-05-21 08:51:18 -07001171 if (lpTrack == 0) {
1172 return false;
1173 }
Paul McLean88e1d862015-04-06 16:36:51 -07001174 return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1175}
1176
Eric Laurent4bcdba82015-05-01 11:37:49 -07001177static jint android_media_AudioTrack_getRoutedDeviceId(
1178 JNIEnv *env, jobject thiz) {
1179
1180 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1181 if (lpTrack == NULL) {
1182 return 0;
1183 }
1184 return (jint)lpTrack->getRoutedDeviceId();
1185}
1186
1187static void android_media_AudioTrack_enableDeviceCallback(
1188 JNIEnv *env, jobject thiz) {
1189
1190 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1191 if (lpTrack == NULL) {
1192 return;
1193 }
1194 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1195 thiz, javaAudioTrackFields.jniData);
1196 if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1197 return;
1198 }
1199 pJniStorage->mDeviceCallback =
1200 new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1201 javaAudioTrackFields.postNativeEventInJava);
1202 lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1203}
1204
1205static void android_media_AudioTrack_disableDeviceCallback(
1206 JNIEnv *env, jobject thiz) {
1207
1208 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1209 if (lpTrack == NULL) {
1210 return;
1211 }
1212 AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1213 thiz, javaAudioTrackFields.jniData);
1214 if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1215 return;
1216 }
1217 lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1218 pJniStorage->mDeviceCallback.clear();
1219}
1220
Andy Hung035d4ec2017-01-24 13:45:02 -08001221// Pass through the arguments to the AudioFlinger track implementation.
1222static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
1223 jobject jconfig, jobject joperation) {
1224 // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
1225 const int VOLUME_SHAPER_INVALID_OPERATION = -38;
1226
1227 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1228 if (lpTrack == nullptr) {
1229 return (jint)VOLUME_SHAPER_INVALID_OPERATION;
1230 }
1231
1232 sp<VolumeShaper::Configuration> configuration;
1233 sp<VolumeShaper::Operation> operation;
1234 if (jconfig != nullptr) {
1235 configuration = VolumeShaperHelper::convertJobjectToConfiguration(
1236 env, gVolumeShaperFields, jconfig);
1237 ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
1238 }
1239 if (joperation != nullptr) {
1240 operation = VolumeShaperHelper::convertJobjectToOperation(
1241 env, gVolumeShaperFields, joperation);
1242 ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
1243 }
1244 VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation);
1245 if (status == INVALID_OPERATION) {
1246 status = VOLUME_SHAPER_INVALID_OPERATION;
1247 }
1248 return (jint)status; // if status < 0 an error, else a VolumeShaper id
1249}
1250
1251// Pass through the arguments to the AudioFlinger track implementation.
1252static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz,
1253 jint id) {
1254 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1255 if (lpTrack == nullptr) {
1256 return (jobject)nullptr;
1257 }
1258
1259 sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id);
1260 if (state.get() == nullptr) {
1261 return (jobject)nullptr;
1262 }
1263 return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
1264}
Eric Laurent4bcdba82015-05-01 11:37:49 -07001265
Previr Rangroo58822be2017-11-28 17:53:54 +11001266static int android_media_AudioTrack_setPresentation(
1267 JNIEnv *env, jobject thiz, jint presentationId, jint programId) {
1268 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1269 if (lpTrack == NULL) {
1270 jniThrowException(env, "java/lang/IllegalStateException",
1271 "AudioTrack not initialized");
1272 return (jint)AUDIO_JAVA_ERROR;
1273 }
1274
1275 return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId);
1276}
1277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278// ----------------------------------------------------------------------------
Eric Laurent2dc54832018-11-20 17:49:43 -08001279static jint android_media_AudioTrack_get_port_id(JNIEnv *env, jobject thiz) {
1280 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1281 if (lpTrack == NULL) {
1282 jniThrowException(env, "java/lang/IllegalStateException",
1283 "AudioTrack not initialized");
1284 return (jint)AUDIO_PORT_HANDLE_NONE;
1285 }
1286 return (jint)lpTrack->getPortId();
1287}
1288
1289// ----------------------------------------------------------------------------
Jean-Michel Trivi157cba42019-01-25 18:40:03 -08001290static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thiz,
1291 jint delayInFrames, jint paddingInFrames) {
1292 sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1293 if (lpTrack == NULL) {
1294 jniThrowException(env, "java/lang/IllegalStateException",
1295 "AudioTrack not initialized");
1296 return;
1297 }
1298 AudioParameter param = AudioParameter();
1299 param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), (int) delayInFrames);
1300 param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), (int) paddingInFrames);
1301 lpTrack->setParameters(param.toString());
1302}
1303
Jean-Michel Trivi157cba42019-01-25 18:40:03 -08001304// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305// ----------------------------------------------------------------------------
Daniel Micay76f6a862015-09-19 17:31:01 -04001306static const JNINativeMethod gMethods[] = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 // name, signature, funcPtr
Michael Chan2de156d2018-04-24 14:33:57 +10001308 {"native_is_direct_output_supported",
1309 "(IIIIIII)Z",
1310 (void *)android_media_AudioTrack_is_direct_output_supported},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 {"native_start", "()V", (void *)android_media_AudioTrack_start},
1312 {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
1313 {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
1314 {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08001315 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
Jean-Michel Trivieac84382014-02-04 14:50:40 -08001316 (void *)android_media_AudioTrack_setup},
1317 {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
1318 {"native_release", "()V", (void *)android_media_AudioTrack_release},
Andy Hung4aacc902015-04-14 15:01:29 -07001319 {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -08001320 {"native_write_native_bytes",
Orion Hodson9b909c32018-11-23 11:05:27 +00001321 "(Ljava/nio/ByteBuffer;IIIZ)I",
Jean-Michel Trivi7ca04522014-02-07 09:39:34 -08001322 (void *)android_media_AudioTrack_write_native_bytes},
Andy Hung4aacc902015-04-14 15:01:29 -07001323 {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1324 {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
Phil Burk10a33e42016-01-08 12:40:41 -08001326 {"native_get_buffer_size_frames",
1327 "()I", (void *)android_media_AudioTrack_get_buffer_size_frames},
1328 {"native_set_buffer_size_frames",
1329 "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames},
1330 {"native_get_buffer_capacity_frames",
1331 "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 {"native_set_playback_rate",
Eric Laurent88e209d2009-07-07 07:10:45 -07001333 "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 {"native_get_playback_rate",
1335 "()I", (void *)android_media_AudioTrack_get_playback_rate},
Wei Jia2d61e2b2015-05-08 15:23:28 -07001336 {"native_set_playback_params",
1337 "(Landroid/media/PlaybackParams;)V",
1338 (void *)android_media_AudioTrack_set_playback_params},
1339 {"native_get_playback_params",
1340 "()Landroid/media/PlaybackParams;",
1341 (void *)android_media_AudioTrack_get_playback_params},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001342 {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos},
1343 {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos},
1344 {"native_set_pos_update_period",
1345 "(I)I", (void *)android_media_AudioTrack_set_pos_update_period},
1346 {"native_get_pos_update_period",
1347 "()I", (void *)android_media_AudioTrack_get_pos_update_period},
1348 {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
1349 {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
Oliver Woodman61dcdf32013-06-26 12:43:36 +01001350 {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
Phil Burk03f61bb2016-01-17 21:49:58 +00001351 {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
Andy Hungebc2c142017-01-12 19:20:29 -08001352 {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
Glenn Kasten948c2e62013-09-04 13:51:29 -07001353 {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
Ray Essick510225b2018-01-24 14:27:16 -08001354 {"native_getMetrics", "()Landroid/os/PersistableBundle;",
1355 (void *)android_media_AudioTrack_native_getMetrics},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
1357 {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
1358 {"native_get_output_sample_rate",
1359 "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate},
1360 {"native_get_min_buff_size",
1361 "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
Eric Laurent7070b362010-07-16 07:43:46 -07001362 {"native_setAuxEffectSendLevel",
Glenn Kasten3009f0b2014-03-28 16:02:26 -07001363 "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel},
Eric Laurent7070b362010-07-16 07:43:46 -07001364 {"native_attachAuxEffect",
1365 "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
Paul McLean88e1d862015-04-06 16:36:51 -07001366 {"native_setOutputDevice", "(I)Z",
1367 (void *)android_media_AudioTrack_setOutputDevice},
Eric Laurent4bcdba82015-05-01 11:37:49 -07001368 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1369 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1370 {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
Andy Hung035d4ec2017-01-24 13:45:02 -08001371 {"native_applyVolumeShaper",
1372 "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
1373 (void *)android_media_AudioTrack_apply_volume_shaper},
1374 {"native_getVolumeShaperState",
1375 "(I)Landroid/media/VolumeShaper$State;",
1376 (void *)android_media_AudioTrack_get_volume_shaper_state},
Previr Rangroo58822be2017-11-28 17:53:54 +11001377 {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
Eric Laurent2dc54832018-11-20 17:49:43 -08001378 {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
Jean-Michel Trivi157cba42019-01-25 18:40:03 -08001379 {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380};
1381
1382
1383// field names found in android/media/AudioTrack.java
1384#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
1386#define JAVA_JNIDATA_FIELD_NAME "mJniData"
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001387#define JAVA_STREAMTYPE_FIELD_NAME "mStreamType"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389// ----------------------------------------------------------------------------
1390// preconditions:
1391// theClass is valid
1392bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1393 const char* constName, int* constVal) {
1394 jfieldID javaConst = NULL;
1395 javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1396 if (javaConst != NULL) {
1397 *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1398 return true;
1399 } else {
Steve Block3762c312012-01-06 19:20:56 +00001400 ALOGE("Can't find %s.%s", className, constName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 return false;
1402 }
1403}
1404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405// ----------------------------------------------------------------------------
1406int register_android_media_AudioTrack(JNIEnv *env)
1407{
Glenn Kasten931fde42016-01-07 15:59:38 -08001408 // must be first
1409 int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1412 javaAudioTrackFields.postNativeEventInJava = NULL;
1413
1414 // Get the AudioTrack class
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001415 jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416
1417 // Get the postEvent method
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001418 javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1419 audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1420 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421
1422 // Get the variables fields
1423 // nativeTrackInJavaObj
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001424 javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1425 audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001426 // jniData
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001427 javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1428 audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001429 // fieldStreamType
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001430 javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1431 audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
Jean-Michel Trivia1d80e32014-06-18 08:18:41 -07001432
Andy Hungfe48e0d2015-04-27 18:14:02 -07001433 env->DeleteLocalRef(audioTrackClass);
1434
Wei Jia2d61e2b2015-05-08 15:23:28 -07001435 // initialize PlaybackParams field info
1436 gPlaybackParamsFields.init(env);
Andy Hungfe48e0d2015-04-27 18:14:02 -07001437
Andy Hung035d4ec2017-01-24 13:45:02 -08001438 gVolumeShaperFields.init(env);
Glenn Kasten931fde42016-01-07 15:59:38 -08001439 return res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001440}
1441
1442
1443// ----------------------------------------------------------------------------