JAudioTrack.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. /*
  2. * Copyright 2018 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_TAG "JAudioTrack"
  17. #include "media/JAudioAttributes.h"
  18. #include "media/JAudioFormat.h"
  19. #include "mediaplayer2/JAudioTrack.h"
  20. #include <android_media_AudioErrors.h>
  21. #include <mediaplayer2/JavaVMHelper.h>
  22. namespace android {
  23. // TODO: Store Java class/methodID as a member variable in the class.
  24. // TODO: Add NULL && Exception checks after every JNI call.
  25. JAudioTrack::JAudioTrack( // < Usages of the arguments are below >
  26. uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
  27. audio_format_t format, // AudioFormat && bufferSizeInBytes
  28. audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
  29. callback_t cbf, // Offload
  30. void* user, // Offload
  31. size_t frameCount, // bufferSizeInBytes
  32. int32_t sessionId, // AudioTrack
  33. const jobject attributes, // AudioAttributes
  34. float maxRequiredSpeed) { // bufferSizeInBytes
  35. JNIEnv *env = JavaVMHelper::getJNIEnv();
  36. jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
  37. mAudioTrackCls = reinterpret_cast<jclass>(env->NewGlobalRef(jAudioTrackCls));
  38. env->DeleteLocalRef(jAudioTrackCls);
  39. maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
  40. int bufferSizeInBytes = 0;
  41. if (sampleRate == 0 || frameCount > 0) {
  42. // Manually calculate buffer size.
  43. bufferSizeInBytes = audio_channel_count_from_out_mask(channelMask)
  44. * audio_bytes_per_sample(format) * (frameCount > 0 ? frameCount : 1);
  45. } else if (sampleRate > 0) {
  46. // Call Java AudioTrack::getMinBufferSize().
  47. jmethodID jGetMinBufferSize =
  48. env->GetStaticMethodID(mAudioTrackCls, "getMinBufferSize", "(III)I");
  49. bufferSizeInBytes = env->CallStaticIntMethod(mAudioTrackCls, jGetMinBufferSize,
  50. sampleRate, outChannelMaskFromNative(channelMask), audioFormatFromNative(format));
  51. }
  52. bufferSizeInBytes = (int) (bufferSizeInBytes * maxRequiredSpeed);
  53. // Create a Java AudioTrack object through its Builder.
  54. jclass jBuilderCls = env->FindClass("android/media/AudioTrack$Builder");
  55. jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
  56. jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
  57. {
  58. sp<JObjectHolder> audioAttributesObj;
  59. if (attributes != NULL) {
  60. audioAttributesObj = new JObjectHolder(attributes);
  61. } else {
  62. audioAttributesObj = new JObjectHolder(
  63. JAudioAttributes::createAudioAttributesObj(env, NULL));
  64. }
  65. jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
  66. "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
  67. jBuilderObj = env->CallObjectMethod(jBuilderObj,
  68. jSetAudioAttributes, audioAttributesObj->getJObject());
  69. }
  70. jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
  71. "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
  72. jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioFormat,
  73. JAudioFormat::createAudioFormatObj(env, sampleRate, format, channelMask));
  74. jmethodID jSetBufferSizeInBytes = env->GetMethodID(jBuilderCls, "setBufferSizeInBytes",
  75. "(I)Landroid/media/AudioTrack$Builder;");
  76. jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetBufferSizeInBytes, bufferSizeInBytes);
  77. // We only use streaming mode of Java AudioTrack.
  78. jfieldID jModeStream = env->GetStaticFieldID(mAudioTrackCls, "MODE_STREAM", "I");
  79. jint transferMode = env->GetStaticIntField(mAudioTrackCls, jModeStream);
  80. jmethodID jSetTransferMode = env->GetMethodID(jBuilderCls, "setTransferMode",
  81. "(I)Landroid/media/AudioTrack$Builder;");
  82. jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetTransferMode,
  83. transferMode /* Java AudioTrack::MODE_STREAM */);
  84. if (sessionId != 0) {
  85. jmethodID jSetSessionId = env->GetMethodID(jBuilderCls, "setSessionId",
  86. "(I)Landroid/media/AudioTrack$Builder;");
  87. jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
  88. }
  89. mFlags = AUDIO_OUTPUT_FLAG_NONE;
  90. if (cbf != NULL) {
  91. jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
  92. "(Z)Landroid/media/AudioTrack$Builder;");
  93. jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
  94. mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
  95. }
  96. jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
  97. jobject jAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
  98. mAudioTrackObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioTrackObj));
  99. env->DeleteLocalRef(jBuilderObj);
  100. if (cbf != NULL) {
  101. // Set offload mode callback
  102. jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
  103. jobject jExecutorObj = createCallbackExecutor();
  104. jmethodID jSetStreamEventCallback = env->GetMethodID(
  105. jAudioTrackCls,
  106. "setStreamEventCallback",
  107. "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
  108. env->CallVoidMethod(
  109. mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
  110. }
  111. }
  112. JAudioTrack::~JAudioTrack() {
  113. JNIEnv *env = JavaVMHelper::getJNIEnv();
  114. env->DeleteGlobalRef(mAudioTrackCls);
  115. env->DeleteGlobalRef(mAudioTrackObj);
  116. }
  117. size_t JAudioTrack::frameCount() {
  118. JNIEnv *env = JavaVMHelper::getJNIEnv();
  119. jmethodID jGetBufferSizeInFrames = env->GetMethodID(
  120. mAudioTrackCls, "getBufferSizeInFrames", "()I");
  121. return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
  122. }
  123. size_t JAudioTrack::channelCount() {
  124. JNIEnv *env = JavaVMHelper::getJNIEnv();
  125. jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
  126. return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
  127. }
  128. uint32_t JAudioTrack::latency() {
  129. // TODO: Currently hard-coded as returning zero.
  130. return 0;
  131. }
  132. status_t JAudioTrack::getPosition(uint32_t *position) {
  133. if (position == NULL) {
  134. return BAD_VALUE;
  135. }
  136. JNIEnv *env = JavaVMHelper::getJNIEnv();
  137. jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
  138. mAudioTrackCls, "getPlaybackHeadPosition", "()I");
  139. *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
  140. return NO_ERROR;
  141. }
  142. status_t JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
  143. JNIEnv *env = JavaVMHelper::getJNIEnv();
  144. jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
  145. jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
  146. jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "J");
  147. jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "J");
  148. jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
  149. "getTimestamp", "(Landroid/media/AudioTimestamp;)Z");
  150. bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
  151. if (!success) {
  152. return NO_INIT;
  153. }
  154. long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
  155. long long nanoTime = env->GetLongField(jAudioTimeStampObj, jNanoTime);
  156. struct timespec ts;
  157. const long long secondToNano = 1000000000LL; // 1E9
  158. ts.tv_sec = nanoTime / secondToNano;
  159. ts.tv_nsec = nanoTime % secondToNano;
  160. timestamp.mTime = ts;
  161. timestamp.mPosition = (uint32_t) framePosition;
  162. return NO_ERROR;
  163. }
  164. status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
  165. // TODO: Implement this after appropriate Java AudioTrack method is available.
  166. return NO_ERROR;
  167. }
  168. status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
  169. // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
  170. // Should we do the same thing?
  171. JNIEnv *env = JavaVMHelper::getJNIEnv();
  172. jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
  173. jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "<init>", "()V");
  174. jobject jPlaybackParamsObj = env->NewObject(jPlaybackParamsCls, jPlaybackParamsCtor);
  175. jmethodID jSetAudioFallbackMode = env->GetMethodID(
  176. jPlaybackParamsCls, "setAudioFallbackMode", "(I)Landroid/media/PlaybackParams;");
  177. jPlaybackParamsObj = env->CallObjectMethod(
  178. jPlaybackParamsObj, jSetAudioFallbackMode, playbackRate.mFallbackMode);
  179. jmethodID jSetAudioStretchMode = env->GetMethodID(
  180. jPlaybackParamsCls, "setAudioStretchMode", "(I)Landroid/media/PlaybackParams;");
  181. jPlaybackParamsObj = env->CallObjectMethod(
  182. jPlaybackParamsObj, jSetAudioStretchMode, playbackRate.mStretchMode);
  183. jmethodID jSetPitch = env->GetMethodID(
  184. jPlaybackParamsCls, "setPitch", "(F)Landroid/media/PlaybackParams;");
  185. jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetPitch, playbackRate.mPitch);
  186. jmethodID jSetSpeed = env->GetMethodID(
  187. jPlaybackParamsCls, "setSpeed", "(F)Landroid/media/PlaybackParams;");
  188. jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetSpeed, playbackRate.mSpeed);
  189. // Set this Java PlaybackParams object into Java AudioTrack.
  190. jmethodID jSetPlaybackParams = env->GetMethodID(
  191. mAudioTrackCls, "setPlaybackParams", "(Landroid/media/PlaybackParams;)V");
  192. env->CallVoidMethod(mAudioTrackObj, jSetPlaybackParams, jPlaybackParamsObj);
  193. // TODO: Should we catch the Java IllegalArgumentException?
  194. return NO_ERROR;
  195. }
  196. const AudioPlaybackRate JAudioTrack::getPlaybackRate() {
  197. JNIEnv *env = JavaVMHelper::getJNIEnv();
  198. jmethodID jGetPlaybackParams = env->GetMethodID(
  199. mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;");
  200. jobject jPlaybackParamsObj = env->CallObjectMethod(mAudioTrackObj, jGetPlaybackParams);
  201. AudioPlaybackRate playbackRate;
  202. jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
  203. jmethodID jGetAudioFallbackMode = env->GetMethodID(
  204. jPlaybackParamsCls, "getAudioFallbackMode", "()I");
  205. // TODO: Should we enable passing AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT?
  206. // The enum is internal only, so it is not defined in PlaybackParmas.java.
  207. // TODO: Is this right way to convert an int to an enum?
  208. playbackRate.mFallbackMode = static_cast<AudioTimestretchFallbackMode>(
  209. env->CallIntMethod(jPlaybackParamsObj, jGetAudioFallbackMode));
  210. jmethodID jGetAudioStretchMode = env->GetMethodID(
  211. jPlaybackParamsCls, "getAudioStretchMode", "()I");
  212. playbackRate.mStretchMode = static_cast<AudioTimestretchStretchMode>(
  213. env->CallIntMethod(jPlaybackParamsObj, jGetAudioStretchMode));
  214. jmethodID jGetPitch = env->GetMethodID(jPlaybackParamsCls, "getPitch", "()F");
  215. playbackRate.mPitch = env->CallFloatMethod(jPlaybackParamsObj, jGetPitch);
  216. jmethodID jGetSpeed = env->GetMethodID(jPlaybackParamsCls, "getSpeed", "()F");
  217. playbackRate.mSpeed = env->CallFloatMethod(jPlaybackParamsObj, jGetSpeed);
  218. return playbackRate;
  219. }
  220. media::VolumeShaper::Status JAudioTrack::applyVolumeShaper(
  221. const sp<media::VolumeShaper::Configuration>& configuration,
  222. const sp<media::VolumeShaper::Operation>& operation) {
  223. jobject jConfigurationObj = createVolumeShaperConfigurationObj(configuration);
  224. jobject jOperationObj = createVolumeShaperOperationObj(operation);
  225. if (jConfigurationObj == NULL || jOperationObj == NULL) {
  226. return media::VolumeShaper::Status(BAD_VALUE);
  227. }
  228. JNIEnv *env = JavaVMHelper::getJNIEnv();
  229. jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper",
  230. "(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;");
  231. jobject jVolumeShaperObj = env->CallObjectMethod(
  232. mAudioTrackObj, jCreateVolumeShaper, jConfigurationObj);
  233. jclass jVolumeShaperCls = env->FindClass("android/media/VolumeShaper");
  234. jmethodID jApply = env->GetMethodID(jVolumeShaperCls, "apply",
  235. "(Landroid/media/VolumeShaper$Operation;)V");
  236. env->CallVoidMethod(jVolumeShaperObj, jApply, jOperationObj);
  237. return media::VolumeShaper::Status(NO_ERROR);
  238. }
  239. status_t JAudioTrack::setAuxEffectSendLevel(float level) {
  240. JNIEnv *env = JavaVMHelper::getJNIEnv();
  241. jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
  242. mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
  243. int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
  244. return javaToNativeStatus(result);
  245. }
  246. status_t JAudioTrack::attachAuxEffect(int effectId) {
  247. JNIEnv *env = JavaVMHelper::getJNIEnv();
  248. jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
  249. int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
  250. return javaToNativeStatus(result);
  251. }
  252. status_t JAudioTrack::setVolume(float left, float right) {
  253. JNIEnv *env = JavaVMHelper::getJNIEnv();
  254. // TODO: Java setStereoVolume is deprecated. Do we really need this method?
  255. jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
  256. int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
  257. return javaToNativeStatus(result);
  258. }
  259. status_t JAudioTrack::setVolume(float volume) {
  260. JNIEnv *env = JavaVMHelper::getJNIEnv();
  261. jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
  262. int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
  263. return javaToNativeStatus(result);
  264. }
  265. status_t JAudioTrack::start() {
  266. JNIEnv *env = JavaVMHelper::getJNIEnv();
  267. jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
  268. // TODO: Should we catch the Java IllegalStateException from play()?
  269. env->CallVoidMethod(mAudioTrackObj, jPlay);
  270. return NO_ERROR;
  271. }
  272. ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) {
  273. if (buffer == NULL) {
  274. return BAD_VALUE;
  275. }
  276. JNIEnv *env = JavaVMHelper::getJNIEnv();
  277. jbyteArray jAudioData = env->NewByteArray(size);
  278. env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer);
  279. jclass jByteBufferCls = env->FindClass("java/nio/ByteBuffer");
  280. jmethodID jWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
  281. jobject jByteBufferObj = env->CallStaticObjectMethod(jByteBufferCls, jWrap, jAudioData);
  282. int writeMode = 0;
  283. if (blocking) {
  284. jfieldID jWriteBlocking = env->GetStaticFieldID(mAudioTrackCls, "WRITE_BLOCKING", "I");
  285. writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteBlocking);
  286. } else {
  287. jfieldID jWriteNonBlocking = env->GetStaticFieldID(
  288. mAudioTrackCls, "WRITE_NON_BLOCKING", "I");
  289. writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteNonBlocking);
  290. }
  291. jmethodID jWrite = env->GetMethodID(mAudioTrackCls, "write", "(Ljava/nio/ByteBuffer;II)I");
  292. int result = env->CallIntMethod(mAudioTrackObj, jWrite, jByteBufferObj, size, writeMode);
  293. if (result >= 0) {
  294. return result;
  295. } else {
  296. return javaToNativeStatus(result);
  297. }
  298. }
  299. void JAudioTrack::stop() {
  300. JNIEnv *env = JavaVMHelper::getJNIEnv();
  301. jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
  302. env->CallVoidMethod(mAudioTrackObj, jStop);
  303. // TODO: Should we catch IllegalStateException?
  304. }
  305. // TODO: Is the right implementation?
  306. bool JAudioTrack::stopped() const {
  307. return !isPlaying();
  308. }
  309. void JAudioTrack::flush() {
  310. JNIEnv *env = JavaVMHelper::getJNIEnv();
  311. jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
  312. env->CallVoidMethod(mAudioTrackObj, jFlush);
  313. }
  314. void JAudioTrack::pause() {
  315. JNIEnv *env = JavaVMHelper::getJNIEnv();
  316. jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
  317. env->CallVoidMethod(mAudioTrackObj, jPause);
  318. // TODO: Should we catch IllegalStateException?
  319. }
  320. bool JAudioTrack::isPlaying() const {
  321. JNIEnv *env = JavaVMHelper::getJNIEnv();
  322. jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
  323. int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
  324. // TODO: In Java AudioTrack, there is no STOPPING state.
  325. // This means while stopping, isPlaying() will return different value in two class.
  326. // - in existing native AudioTrack: true
  327. // - in JAudioTrack: false
  328. // If not okay, also modify the implementation of stopped().
  329. jfieldID jPlayStatePlaying = env->GetStaticFieldID(mAudioTrackCls, "PLAYSTATE_PLAYING", "I");
  330. int statePlaying = env->GetStaticIntField(mAudioTrackCls, jPlayStatePlaying);
  331. return currentPlayState == statePlaying;
  332. }
  333. uint32_t JAudioTrack::getSampleRate() {
  334. JNIEnv *env = JavaVMHelper::getJNIEnv();
  335. jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
  336. return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
  337. }
  338. status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) {
  339. if (duration == nullptr) {
  340. return BAD_VALUE;
  341. }
  342. JNIEnv *env = JavaVMHelper::getJNIEnv();
  343. jmethodID jGetBufferSizeInFrames = env->GetMethodID(
  344. mAudioTrackCls, "getBufferSizeInFrames", "()I");
  345. int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
  346. const double secondToMicro = 1000000LL; // 1E6
  347. int sampleRate = JAudioTrack::getSampleRate();
  348. float speed = JAudioTrack::getPlaybackRate().mSpeed;
  349. *duration = (int64_t) (bufferSizeInFrames * secondToMicro / (sampleRate * speed));
  350. return NO_ERROR;
  351. }
  352. audio_format_t JAudioTrack::format() {
  353. JNIEnv *env = JavaVMHelper::getJNIEnv();
  354. jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
  355. int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
  356. return audioFormatToNative(javaFormat);
  357. }
  358. size_t JAudioTrack::frameSize() {
  359. JNIEnv *env = JavaVMHelper::getJNIEnv();
  360. jmethodID jGetFormat = env->GetMethodID(mAudioTrackCls,
  361. "getFormat", "()Landroid/media/AudioFormat;");
  362. jobject jAudioFormatObj = env->CallObjectMethod(mAudioTrackObj, jGetFormat);
  363. jclass jAudioFormatCls = env->FindClass("android/media/AudioFormat");
  364. jmethodID jGetFrameSizeInBytes = env->GetMethodID(
  365. jAudioFormatCls, "getFrameSizeInBytes", "()I");
  366. jint javaFrameSizeInBytes = env->CallIntMethod(jAudioFormatObj, jGetFrameSizeInBytes);
  367. return (size_t)javaFrameSizeInBytes;
  368. }
  369. status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
  370. {
  371. String8 result;
  372. result.append(" JAudioTrack::dump\n");
  373. // TODO: Remove logs that includes unavailable information from below.
  374. // result.appendFormat(" status(%d), state(%d), session Id(%d), flags(%#x)\n",
  375. // mStatus, mState, mSessionId, mFlags);
  376. // result.appendFormat(" format(%#x), channel mask(%#x), channel count(%u)\n",
  377. // format(), mChannelMask, channelCount());
  378. // result.appendFormat(" sample rate(%u), original sample rate(%u), speed(%f)\n",
  379. // getSampleRate(), mOriginalSampleRate, mPlaybackRate.mSpeed);
  380. // result.appendFormat(" frame count(%zu), req. frame count(%zu)\n",
  381. // frameCount(), mReqFrameCount);
  382. // result.appendFormat(" notif. frame count(%u), req. notif. frame count(%u),"
  383. // " req. notif. per buff(%u)\n",
  384. // mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq);
  385. // result.appendFormat(" latency (%d), selected device Id(%d), routed device Id(%d)\n",
  386. // latency(), mSelectedDeviceId, getRoutedDeviceId());
  387. // result.appendFormat(" output(%d) AF latency (%u) AF frame count(%zu) AF SampleRate(%u)\n",
  388. // mOutput, mAfLatency, mAfFrameCount, mAfSampleRate);
  389. ::write(fd, result.string(), result.size());
  390. return NO_ERROR;
  391. }
  392. jobject JAudioTrack::getRoutedDevice() {
  393. JNIEnv *env = JavaVMHelper::getJNIEnv();
  394. jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
  395. "()Landroid/media/AudioDeviceInfo;");
  396. return env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
  397. }
  398. int32_t JAudioTrack::getAudioSessionId() {
  399. JNIEnv *env = JavaVMHelper::getJNIEnv();
  400. jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
  401. jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
  402. return sessionId;
  403. }
  404. status_t JAudioTrack::setPreferredDevice(jobject device) {
  405. JNIEnv *env = JavaVMHelper::getJNIEnv();
  406. jmethodID jSetPreferredDeviceId = env->GetMethodID(mAudioTrackCls, "setPreferredDevice",
  407. "(Landroid/media/AudioDeviceInfo;)Z");
  408. jboolean result = env->CallBooleanMethod(mAudioTrackObj, jSetPreferredDeviceId, device);
  409. return result == true ? NO_ERROR : BAD_VALUE;
  410. }
  411. audio_stream_type_t JAudioTrack::getAudioStreamType() {
  412. JNIEnv *env = JavaVMHelper::getJNIEnv();
  413. jmethodID jGetAudioAttributes = env->GetMethodID(mAudioTrackCls, "getAudioAttributes",
  414. "()Landroid/media/AudioAttributes;");
  415. jobject jAudioAttributes = env->CallObjectMethod(mAudioTrackObj, jGetAudioAttributes);
  416. jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes");
  417. jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls,
  418. "getVolumeControlStream", "()I");
  419. int javaAudioStreamType = env->CallIntMethod(jAudioAttributes, jGetVolumeControlStream);
  420. return (audio_stream_type_t)javaAudioStreamType;
  421. }
  422. status_t JAudioTrack::pendingDuration(int32_t *msec) {
  423. if (msec == nullptr) {
  424. return BAD_VALUE;
  425. }
  426. bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
  427. if (!isPurePcmData) {
  428. return INVALID_OPERATION;
  429. }
  430. // TODO: Need to know the difference btw. client and server time.
  431. // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
  432. // (copied from AudioTrack.cpp)
  433. // ExtendedTimestamp ets;
  434. // ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
  435. // if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
  436. // int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
  437. // - ets.mPosition[location];
  438. // if (diff < 0) {
  439. // *msec = 0;
  440. // } else {
  441. // // ms is the playback time by frames
  442. // int64_t ms = (int64_t)((double)diff * 1000 /
  443. // ((double)mSampleRate * mPlaybackRate.mSpeed));
  444. // // clockdiff is the timestamp age (negative)
  445. // int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
  446. // ets.mTimeNs[location]
  447. // + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
  448. // - systemTime(SYSTEM_TIME_MONOTONIC);
  449. //
  450. // //ALOGV("ms: %lld clockdiff: %lld", (long long)ms, (long long)clockdiff);
  451. // static const int NANOS_PER_MILLIS = 1000000;
  452. // *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
  453. // }
  454. // return NO_ERROR;
  455. // }
  456. return NO_ERROR;
  457. }
  458. status_t JAudioTrack::addAudioDeviceCallback(jobject listener, jobject handler) {
  459. JNIEnv *env = JavaVMHelper::getJNIEnv();
  460. jmethodID jAddOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
  461. "addOnRoutingChangedListener",
  462. "(Landroid/media/AudioRouting$OnRoutingChangedListener;Landroid/os/Handler;)V");
  463. env->CallVoidMethod(mAudioTrackObj, jAddOnRoutingChangedListener, listener, handler);
  464. return NO_ERROR;
  465. }
  466. status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) {
  467. JNIEnv *env = JavaVMHelper::getJNIEnv();
  468. jmethodID jRemoveOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
  469. "removeOnRoutingChangedListener",
  470. "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V");
  471. env->CallVoidMethod(mAudioTrackObj, jRemoveOnRoutingChangedListener, listener);
  472. return NO_ERROR;
  473. }
  474. void JAudioTrack::registerRoutingDelegates(
  475. Vector<std::pair<sp<JObjectHolder>, sp<JObjectHolder>>>& routingDelegates) {
  476. for (auto it = routingDelegates.begin(); it != routingDelegates.end(); it++) {
  477. addAudioDeviceCallback(it->second->getJObject(), getHandler(it->second->getJObject()));
  478. }
  479. }
  480. /////////////////////////////////////////////////////////////
  481. /// Static methods begin ///
  482. /////////////////////////////////////////////////////////////
  483. jobject JAudioTrack::getListener(const jobject routingDelegateObj) {
  484. JNIEnv *env = JavaVMHelper::getJNIEnv();
  485. jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
  486. jmethodID jGetListener = env->GetMethodID(jRoutingDelegateCls,
  487. "getListener", "()Landroid/media/AudioRouting$OnRoutingChangedListener;");
  488. return env->CallObjectMethod(routingDelegateObj, jGetListener);
  489. }
  490. jobject JAudioTrack::getHandler(const jobject routingDelegateObj) {
  491. JNIEnv *env = JavaVMHelper::getJNIEnv();
  492. jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
  493. jmethodID jGetHandler = env->GetMethodID(jRoutingDelegateCls,
  494. "getHandler", "()Landroid/os/Handler;");
  495. return env->CallObjectMethod(routingDelegateObj, jGetHandler);
  496. }
  497. jobject JAudioTrack::findByKey(
  498. Vector<std::pair<sp<JObjectHolder>, sp<JObjectHolder>>>& mp, const jobject key) {
  499. JNIEnv *env = JavaVMHelper::getJNIEnv();
  500. for (auto it = mp.begin(); it != mp.end(); it++) {
  501. if (env->IsSameObject(it->first->getJObject(), key)) {
  502. return it->second->getJObject();
  503. }
  504. }
  505. return nullptr;
  506. }
  507. void JAudioTrack::eraseByKey(
  508. Vector<std::pair<sp<JObjectHolder>, sp<JObjectHolder>>>& mp, const jobject key) {
  509. JNIEnv *env = JavaVMHelper::getJNIEnv();
  510. for (auto it = mp.begin(); it != mp.end(); it++) {
  511. if (env->IsSameObject(it->first->getJObject(), key)) {
  512. mp.erase(it);
  513. return;
  514. }
  515. }
  516. }
  517. /////////////////////////////////////////////////////////////
  518. /// Private method begins ///
  519. /////////////////////////////////////////////////////////////
  520. jobject JAudioTrack::createVolumeShaperConfigurationObj(
  521. const sp<media::VolumeShaper::Configuration>& config) {
  522. // TODO: Java VolumeShaper's setId() / setOptionFlags() are hidden.
  523. if (config == NULL || config->getType() == media::VolumeShaper::Configuration::TYPE_ID) {
  524. return NULL;
  525. }
  526. JNIEnv *env = JavaVMHelper::getJNIEnv();
  527. // Referenced "android_media_VolumeShaper.h".
  528. jfloatArray xarray = nullptr;
  529. jfloatArray yarray = nullptr;
  530. if (config->getType() == media::VolumeShaper::Configuration::TYPE_SCALE) {
  531. // convert curve arrays
  532. xarray = env->NewFloatArray(config->size());
  533. yarray = env->NewFloatArray(config->size());
  534. float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
  535. float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
  536. float *xptr = x, *yptr = y;
  537. for (const auto &pt : *config.get()) {
  538. *xptr++ = pt.first;
  539. *yptr++ = pt.second;
  540. }
  541. env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
  542. env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
  543. }
  544. jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Configuration$Builder");
  545. jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
  546. jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
  547. jmethodID jSetDuration = env->GetMethodID(jBuilderCls, "setDuration",
  548. "(L)Landroid/media/VolumeShaper$Configuration$Builder;");
  549. jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetDuration, (jlong) config->getDurationMs());
  550. jmethodID jSetInterpolatorType = env->GetMethodID(jBuilderCls, "setInterpolatorType",
  551. "(I)Landroid/media/VolumeShaper$Configuration$Builder;");
  552. jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetInterpolatorType,
  553. config->getInterpolatorType());
  554. jmethodID jSetCurve = env->GetMethodID(jBuilderCls, "setCurve",
  555. "([F[F)Landroid/media/VolumeShaper$Configuration$Builder;");
  556. jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetCurve, xarray, yarray);
  557. jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
  558. "()Landroid/media/VolumeShaper$Configuration;");
  559. return env->CallObjectMethod(jBuilderObj, jBuild);
  560. }
  561. jobject JAudioTrack::createVolumeShaperOperationObj(
  562. const sp<media::VolumeShaper::Operation>& operation) {
  563. JNIEnv *env = JavaVMHelper::getJNIEnv();
  564. jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder");
  565. jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
  566. jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
  567. // Set XOffset
  568. jmethodID jSetXOffset = env->GetMethodID(jBuilderCls, "setXOffset",
  569. "(F)Landroid/media/VolumeShaper$Operation$Builder;");
  570. jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetXOffset, operation->getXOffset());
  571. int32_t flags = operation->getFlags();
  572. if (operation->getReplaceId() >= 0) {
  573. jmethodID jReplace = env->GetMethodID(jBuilderCls, "replace",
  574. "(IB)Landroid/media/VolumeShaper$Operation$Builder;");
  575. bool join = (flags | media::VolumeShaper::Operation::FLAG_JOIN) != 0;
  576. jBuilderObj = env->CallObjectMethod(jBuilderCls, jReplace, operation->getReplaceId(), join);
  577. }
  578. if (flags | media::VolumeShaper::Operation::FLAG_REVERSE) {
  579. jmethodID jReverse = env->GetMethodID(jBuilderCls, "reverse",
  580. "()Landroid/media/VolumeShaper$Operation$Builder;");
  581. jBuilderObj = env->CallObjectMethod(jBuilderCls, jReverse);
  582. }
  583. // TODO: VolumeShaper Javadoc says "Do not call terminate() directly". Can we call this?
  584. if (flags | media::VolumeShaper::Operation::FLAG_TERMINATE) {
  585. jmethodID jTerminate = env->GetMethodID(jBuilderCls, "terminate",
  586. "()Landroid/media/VolumeShaper$Operation$Builder;");
  587. jBuilderObj = env->CallObjectMethod(jBuilderCls, jTerminate);
  588. }
  589. if (flags | media::VolumeShaper::Operation::FLAG_DELAY) {
  590. jmethodID jDefer = env->GetMethodID(jBuilderCls, "defer",
  591. "()Landroid/media/VolumeShaper$Operation$Builder;");
  592. jBuilderObj = env->CallObjectMethod(jBuilderCls, jDefer);
  593. }
  594. if (flags | media::VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) {
  595. jmethodID jCreateIfNeeded = env->GetMethodID(jBuilderCls, "createIfNeeded",
  596. "()Landroid/media/VolumeShaper$Operation$Builder;");
  597. jBuilderObj = env->CallObjectMethod(jBuilderCls, jCreateIfNeeded);
  598. }
  599. // TODO: Handle error case (can it be NULL?)
  600. jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
  601. "()Landroid/media/VolumeShaper$Operation;");
  602. return env->CallObjectMethod(jBuilderObj, jBuild);
  603. }
  604. jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
  605. JNIEnv *env = JavaVMHelper::getJNIEnv();
  606. jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2$StreamEventCallback");
  607. jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
  608. jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
  609. return jCallbackObj;
  610. }
  611. jobject JAudioTrack::createCallbackExecutor() {
  612. JNIEnv *env = JavaVMHelper::getJNIEnv();
  613. jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
  614. jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
  615. "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
  616. jobject jSingleThreadExecutorObj =
  617. env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
  618. return jSingleThreadExecutorObj;
  619. }
  620. status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
  621. switch (javaStatus) {
  622. case AUDIO_JAVA_SUCCESS:
  623. return NO_ERROR;
  624. case AUDIO_JAVA_BAD_VALUE:
  625. return BAD_VALUE;
  626. case AUDIO_JAVA_INVALID_OPERATION:
  627. return INVALID_OPERATION;
  628. case AUDIO_JAVA_PERMISSION_DENIED:
  629. return PERMISSION_DENIED;
  630. case AUDIO_JAVA_NO_INIT:
  631. return NO_INIT;
  632. case AUDIO_JAVA_WOULD_BLOCK:
  633. return WOULD_BLOCK;
  634. case AUDIO_JAVA_DEAD_OBJECT:
  635. return DEAD_OBJECT;
  636. default:
  637. return UNKNOWN_ERROR;
  638. }
  639. }
  640. } // namespace android